From 39e144dd05fab2310e1c5f3a63c9cfe68e6bf2cf Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Fri, 2 Oct 2020 19:20:53 +0200 Subject: [PATCH 01/93] Fix format of debug messages in tlscommon (#21482) --- libbeat/common/transport/tlscommon/tls.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libbeat/common/transport/tlscommon/tls.go b/libbeat/common/transport/tlscommon/tls.go index 3616a9f07e4..ba44310727c 100644 --- a/libbeat/common/transport/tlscommon/tls.go +++ b/libbeat/common/transport/tlscommon/tls.go @@ -66,7 +66,7 @@ func LoadCertificate(config *CertificateConfig) (*tls.Certificate, error) { return nil, err } - log.Debugf("tls", "loading certificate: %v and key %v", certificate, key) + log.Debugf("Loading certificate: %v and key %v", certificate, key) return &cert, nil } @@ -169,7 +169,7 @@ func LoadCertificateAuthorities(CAs []string) (*x509.CertPool, []error) { errors = append(errors, fmt.Errorf("%v adding %v to the list of known CAs", ErrNotACertificate, r)) continue } - log.Debugf("tls", "successfully loaded CA certificate: %v", r) + log.Debugf("Successfully loaded CA certificate: %v", r) } return roots, errors From bcb8da063f4dfe68c6ce413a7e7494a7a429ab8e Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Fri, 2 Oct 2020 18:29:22 +0100 Subject: [PATCH 02/93] Revert "Revert "[JJBB] Set shallow cloning to 10 (#21409)" (#21447)" (#21467) This reverts commit 514809a13cd078be71c0bccac03dccc0cc81f0e9. --- .ci/jobs/apm-beats-update.yml | 2 +- .ci/jobs/beats-tester.yml | 2 +- .ci/jobs/beats-windows-mbp.yml | 2 +- .ci/jobs/beats.yml | 2 +- .ci/jobs/golang-crossbuild-mbp.yml | 2 +- .ci/jobs/packaging.yml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.ci/jobs/apm-beats-update.yml b/.ci/jobs/apm-beats-update.yml index 8bdc322f65a..2ae688ffab7 100644 --- a/.ci/jobs/apm-beats-update.yml +++ b/.ci/jobs/apm-beats-update.yml @@ -48,7 +48,7 @@ before: true prune: true shallow-clone: true - depth: 3 + depth: 10 do-not-fetch-tags: true submodule: disable: false diff --git a/.ci/jobs/beats-tester.yml b/.ci/jobs/beats-tester.yml index 522abfa5e5c..808123a225e 100644 --- a/.ci/jobs/beats-tester.yml +++ b/.ci/jobs/beats-tester.yml @@ -44,7 +44,7 @@ before: true prune: true shallow-clone: true - depth: 3 + depth: 10 do-not-fetch-tags: true submodule: disable: false diff --git a/.ci/jobs/beats-windows-mbp.yml b/.ci/jobs/beats-windows-mbp.yml index 64efa009979..7ea26c9ba18 100644 --- a/.ci/jobs/beats-windows-mbp.yml +++ b/.ci/jobs/beats-windows-mbp.yml @@ -44,7 +44,7 @@ before: true prune: true shallow-clone: true - depth: 4 + depth: 10 do-not-fetch-tags: true submodule: disable: false diff --git a/.ci/jobs/beats.yml b/.ci/jobs/beats.yml index b075d8bbdf2..6f59a9bcdf8 100644 --- a/.ci/jobs/beats.yml +++ b/.ci/jobs/beats.yml @@ -46,7 +46,7 @@ before: true prune: true shallow-clone: true - depth: 3 + depth: 10 do-not-fetch-tags: true submodule: disable: false diff --git a/.ci/jobs/golang-crossbuild-mbp.yml b/.ci/jobs/golang-crossbuild-mbp.yml index 46303790610..45175d169f6 100644 --- a/.ci/jobs/golang-crossbuild-mbp.yml +++ b/.ci/jobs/golang-crossbuild-mbp.yml @@ -31,7 +31,7 @@ before: true prune: true shallow-clone: true - depth: 4 + depth: 10 do-not-fetch-tags: true submodule: disable: false diff --git a/.ci/jobs/packaging.yml b/.ci/jobs/packaging.yml index 0dce4d4672b..fd6fb9f90c6 100644 --- a/.ci/jobs/packaging.yml +++ b/.ci/jobs/packaging.yml @@ -44,7 +44,7 @@ before: true prune: true shallow-clone: true - depth: 3 + depth: 10 do-not-fetch-tags: true submodule: disable: false From b0236ee8afacb87e47a98ab075b2b189e5911ff1 Mon Sep 17 00:00:00 2001 From: Fae Charlton Date: Fri, 2 Oct 2020 16:30:06 -0400 Subject: [PATCH 03/93] [libbeat] Add configurable exponential backoff for disk queue write errors (#21493) --- libbeat/publisher/queue/diskqueue/config.go | 52 +++++++++++++++++-- .../publisher/queue/diskqueue/deleter_loop.go | 9 +++- libbeat/publisher/queue/diskqueue/segments.go | 9 +++- libbeat/publisher/queue/diskqueue/util.go | 22 ++++++-- .../publisher/queue/diskqueue/writer_loop.go | 28 ++++++++-- 5 files changed, 104 insertions(+), 16 deletions(-) diff --git a/libbeat/publisher/queue/diskqueue/config.go b/libbeat/publisher/queue/diskqueue/config.go index 6a165a665db..b8ef456d03d 100644 --- a/libbeat/publisher/queue/diskqueue/config.go +++ b/libbeat/publisher/queue/diskqueue/config.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" "path/filepath" + "time" "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/common/cfgtype" @@ -61,16 +62,26 @@ type Settings struct { // A listener that should be sent ACKs when an event is successfully // written to disk. WriteToDiskListener queue.ACKListener + + // RetryInterval specifies how long to wait before retrying a fatal error + // writing to disk. If MaxRetryInterval is nonzero, subsequent retries will + // use exponential backoff up to the specified limit. + RetryInterval time.Duration + MaxRetryInterval time.Duration } // userConfig holds the parameters for a disk queue that are configurable // by the end user in the beats yml file. type userConfig struct { - Path string `config:"path"` - MaxSize cfgtype.ByteSize `config:"max_size" validate:"required"` - SegmentSize *cfgtype.ByteSize `config:"segment_size"` - ReadAheadLimit *int `config:"read_ahead"` - WriteAheadLimit *int `config:"write_ahead"` + Path string `config:"path"` + MaxSize cfgtype.ByteSize `config:"max_size" validate:"required"` + SegmentSize *cfgtype.ByteSize `config:"segment_size"` + + ReadAheadLimit *int `config:"read_ahead"` + WriteAheadLimit *int `config:"write_ahead"` + + RetryInterval *time.Duration `config:"retry_interval" validate:"positive"` + MaxRetryInterval *time.Duration `config:"max_retry_interval" validate:"positive"` } func (c *userConfig) Validate() error { @@ -96,6 +107,13 @@ func (c *userConfig) Validate() error { "Disk queue segment_size (%d) cannot be less than 1MB", *c.SegmentSize) } + if c.RetryInterval != nil && c.MaxRetryInterval != nil && + *c.MaxRetryInterval < *c.RetryInterval { + return fmt.Errorf( + "Disk queue max_retry_interval (%v) can't be less than retry_interval (%v)", + *c.MaxRetryInterval, *c.RetryInterval) + } + return nil } @@ -108,6 +126,9 @@ func DefaultSettings() Settings { ReadAheadLimit: 512, WriteAheadLimit: 2048, + + RetryInterval: 1 * time.Second, + MaxRetryInterval: 30 * time.Second, } } @@ -137,6 +158,13 @@ func SettingsForUserConfig(config *common.Config) (Settings, error) { settings.WriteAheadLimit = *userConfig.WriteAheadLimit } + if userConfig.RetryInterval != nil { + settings.RetryInterval = *userConfig.RetryInterval + } + if userConfig.MaxRetryInterval != nil { + settings.MaxRetryInterval = *userConfig.RetryInterval + } + return settings, nil } @@ -164,3 +192,17 @@ func (settings Settings) segmentPath(segmentID segmentID) string { func (settings Settings) maxSegmentOffset() segmentOffset { return segmentOffset(settings.MaxSegmentSize - segmentHeaderSize) } + +// Given a retry interval, nextRetryInterval returns the next higher level +// of backoff. +func (settings Settings) nextRetryInterval( + currentInterval time.Duration, +) time.Duration { + if settings.MaxRetryInterval > 0 { + currentInterval *= 2 + if currentInterval > settings.MaxRetryInterval { + currentInterval = settings.MaxRetryInterval + } + } + return currentInterval +} diff --git a/libbeat/publisher/queue/diskqueue/deleter_loop.go b/libbeat/publisher/queue/diskqueue/deleter_loop.go index 4e685285948..3948d200cbc 100644 --- a/libbeat/publisher/queue/diskqueue/deleter_loop.go +++ b/libbeat/publisher/queue/diskqueue/deleter_loop.go @@ -57,6 +57,7 @@ func newDeleterLoop(settings Settings) *deleterLoop { } func (dl *deleterLoop) run() { + currentRetryInterval := dl.settings.RetryInterval for { request, ok := <-dl.requestChan if !ok { @@ -87,10 +88,14 @@ func (dl *deleterLoop) run() { // The delay can be interrupted if the request channel is closed, // indicating queue shutdown. select { - // TODO: make the retry interval configurable. - case <-time.After(time.Second): + case <-time.After(currentRetryInterval): case <-dl.requestChan: } + currentRetryInterval = + dl.settings.nextRetryInterval(currentRetryInterval) + } else { + // If we made progress, reset the retry interval. + currentRetryInterval = dl.settings.RetryInterval } dl.responseChan <- deleterLoopResponse{ results: results, diff --git a/libbeat/publisher/queue/diskqueue/segments.go b/libbeat/publisher/queue/diskqueue/segments.go index 5ce0dc49962..617b089110e 100644 --- a/libbeat/publisher/queue/diskqueue/segments.go +++ b/libbeat/publisher/queue/diskqueue/segments.go @@ -207,10 +207,15 @@ func (segment *queueSegment) getWriter( // retry callback returns true. This is used for timed retries when // creating a queue segment from the writer loop. func (segment *queueSegment) getWriterWithRetry( - queueSettings Settings, retry func(error) bool, + queueSettings Settings, retry func(err error, firstTime bool) bool, ) (*os.File, error) { + firstTime := true file, err := segment.getWriter(queueSettings) - for err != nil && retry(err) { + for err != nil && retry(err, firstTime) { + // Set firstTime to false so the retry callback can perform backoff + // etc if needed. + firstTime = false + // Try again file, err = segment.getWriter(queueSettings) } diff --git a/libbeat/publisher/queue/diskqueue/util.go b/libbeat/publisher/queue/diskqueue/util.go index 60c529a9992..c54a26154e8 100644 --- a/libbeat/publisher/queue/diskqueue/util.go +++ b/libbeat/publisher/queue/diskqueue/util.go @@ -69,16 +69,32 @@ func writeErrorIsRetriable(err error) bool { // "wrapped" field in-place as long as it isn't captured by the callback. type callbackRetryWriter struct { wrapped io.Writer - retry func(error) bool + + // The retry callback is called with the error that was produced and whether + // this is the first (subsequent) error arising from this particular + // write call. + retry func(err error, firstTime bool) bool } func (w callbackRetryWriter) Write(p []byte) (int, error) { + // firstTime tracks whether the current error is the first subsequent error + // being passed to the retry callback. This is so that the callback can + // reset its internal counters in case it is using exponential backoff or + // a retry limit. + firstTime := true bytesWritten := 0 writer := w.wrapped n, err := writer.Write(p) for n < len(p) { - if err != nil && !w.retry(err) { - return bytesWritten + n, err + if err != nil { + shouldRetry := w.retry(err, firstTime) + firstTime = false + if !shouldRetry { + return bytesWritten + n, err + } + } else { + // If we made progress without an error, reset firstTime. + firstTime = true } // Advance p and try again. bytesWritten += n diff --git a/libbeat/publisher/queue/diskqueue/writer_loop.go b/libbeat/publisher/queue/diskqueue/writer_loop.go index b42e4573cab..ff1ff97616a 100644 --- a/libbeat/publisher/queue/diskqueue/writer_loop.go +++ b/libbeat/publisher/queue/diskqueue/writer_loop.go @@ -82,6 +82,8 @@ type writerLoop struct { // The file handle corresponding to currentSegment. When currentSegment // changes, this handle is closed and a new one is created. outputFile *os.File + + currentRetryInterval time.Duration } func newWriterLoop(logger *logp.Logger, settings Settings) *writerLoop { @@ -91,6 +93,8 @@ func newWriterLoop(logger *logp.Logger, settings Settings) *writerLoop { requestChan: make(chan writerLoopRequest, 1), responseChan: make(chan writerLoopResponse), + + currentRetryInterval: settings.RetryInterval, } } @@ -215,14 +219,31 @@ outerLoop: return append(bytesWritten, curBytesWritten) } -// retryCallback is called (by way of retryCallbackWriter) when there is +func (wl *writerLoop) applyRetryBackoff() { + wl.currentRetryInterval = + wl.settings.nextRetryInterval(wl.currentRetryInterval) +} + +func (wl *writerLoop) resetRetryBackoff() { + wl.currentRetryInterval = wl.settings.RetryInterval +} + +// retryCallback is called (by way of callbackRetryWriter) when there is // an error writing to a segment file. It pauses for a configurable // interval and returns true if the operation should be retried (which // it always should, unless the queue is being closed). -func (wl *writerLoop) retryCallback(err error) bool { +func (wl *writerLoop) retryCallback(err error, firstTime bool) bool { + if firstTime { + // Reset any exponential backoff in the retry interval. + wl.resetRetryBackoff() + } if writeErrorIsRetriable(err) { return true } + // If this error isn't immediately retriable, increase the exponential + // backoff afterwards. + defer wl.applyRetryBackoff() + // If the error is not immediately retriable, log the error // and wait for the retry interval before trying again, but // abort if the queue is closed (indicated by the request channel @@ -230,8 +251,7 @@ func (wl *writerLoop) retryCallback(err error) bool { wl.logger.Errorf("Writing to segment %v: %v", wl.currentSegment.id, err) select { - case <-time.After(time.Second): - // TODO: use a configurable interval here + case <-time.After(wl.currentRetryInterval): return true case <-wl.requestChan: return false From 7e36f5c6b7507b4d124db561771d886dc0994b38 Mon Sep 17 00:00:00 2001 From: kaiyan-sheng Date: Fri, 2 Oct 2020 15:08:30 -0600 Subject: [PATCH 04/93] Migrate S3 Input to Filebeat Input V2 (#20005) *moving s3 input to v2 input API Co-authored-by: urso --- CHANGELOG.next.asciidoc | 1 + .../filebeat/input/default-inputs/inputs.go | 2 + x-pack/filebeat/input/s3/collector.go | 635 +++++++++++++++ .../s3/{input_test.go => collector_test.go} | 12 +- x-pack/filebeat/input/s3/config.go | 17 +- x-pack/filebeat/input/s3/input.go | 766 ++---------------- .../filebeat/input/s3/s3_integration_test.go | 302 ++----- 7 files changed, 797 insertions(+), 938 deletions(-) create mode 100644 x-pack/filebeat/input/s3/collector.go rename x-pack/filebeat/input/s3/{input_test.go => collector_test.go} (97%) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index a96a23db9d8..fb1fae91eb0 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -603,6 +603,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Always attempt community_id processor on zeek module {pull}21155[21155] - Add related.hosts ecs field to all modules {pull}21160[21160] - Keep cursor state between httpjson input restarts {pull}20751[20751] +- Convert aws s3 to v2 input {pull}20005[20005] *Heartbeat* diff --git a/x-pack/filebeat/input/default-inputs/inputs.go b/x-pack/filebeat/input/default-inputs/inputs.go index cd8562560da..4779b452f1d 100644 --- a/x-pack/filebeat/input/default-inputs/inputs.go +++ b/x-pack/filebeat/input/default-inputs/inputs.go @@ -14,6 +14,7 @@ import ( "github.com/elastic/beats/v7/x-pack/filebeat/input/http_endpoint" "github.com/elastic/beats/v7/x-pack/filebeat/input/httpjson" "github.com/elastic/beats/v7/x-pack/filebeat/input/o365audit" + "github.com/elastic/beats/v7/x-pack/filebeat/input/s3" ) func Init(info beat.Info, log *logp.Logger, store beater.StateStore) []v2.Plugin { @@ -29,5 +30,6 @@ func xpackInputs(info beat.Info, log *logp.Logger, store beater.StateStore) []v2 http_endpoint.Plugin(), httpjson.Plugin(log, store), o365audit.Plugin(log, store), + s3.Plugin(), } } diff --git a/x-pack/filebeat/input/s3/collector.go b/x-pack/filebeat/input/s3/collector.go new file mode 100644 index 00000000000..2976dd52a5b --- /dev/null +++ b/x-pack/filebeat/input/s3/collector.go @@ -0,0 +1,635 @@ +// 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 s3 + +import ( + "bufio" + "compress/gzip" + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "sync" + "time" + + awssdk "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws/awserr" + "github.com/aws/aws-sdk-go-v2/service/s3" + "github.com/aws/aws-sdk-go-v2/service/s3/s3iface" + "github.com/aws/aws-sdk-go-v2/service/sqs" + "github.com/aws/aws-sdk-go-v2/service/sqs/sqsiface" + + "github.com/elastic/beats/v7/libbeat/beat" + "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/logp" + "github.com/elastic/go-concert/unison" +) + +type s3Collector struct { + cancellation context.Context + logger *logp.Logger + + config *config + visibilityTimeout int64 + + sqs sqsiface.ClientAPI + s3 s3iface.ClientAPI + publisher beat.Client +} + +type s3Info struct { + name string + key string + region string + arn string + expandEventListFromField string +} + +type bucket struct { + Name string `json:"name"` + Arn string `json:"arn"` +} + +type object struct { + Key string `json:"key"` +} + +type s3BucketObject struct { + bucket `json:"bucket"` + object `json:"object"` +} + +type sqsMessage struct { + Records []struct { + EventSource string `json:"eventSource"` + AwsRegion string `json:"awsRegion"` + EventName string `json:"eventName"` + S3 s3BucketObject `json:"s3"` + } `json:"Records"` +} + +type s3Context struct { + mux sync.Mutex + refs int + err error // first error witnessed or multi error + errC chan error +} + +var ( + // The maximum number of messages to return. Amazon SQS never returns more messages + // than this value (however, fewer messages might be returned). + maxNumberOfMessage uint8 = 10 + + // The duration (in seconds) for which the call waits for a message to arrive + // in the queue before returning. If a message is available, the call returns + // sooner than WaitTimeSeconds. If no messages are available and the wait time + // expires, the call returns successfully with an empty list of messages. + waitTimeSecond uint8 = 10 +) + +func (c *s3Collector) run() { + defer c.logger.Info("s3 input worker has stopped.") + c.logger.Info("s3 input worker has started.") + for c.cancellation.Err() == nil { + // receive messages from sqs + output, err := c.receiveMessage(c.sqs, c.visibilityTimeout) + if err != nil { + if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == awssdk.ErrCodeRequestCanceled { + continue + } + c.logger.Error("SQS ReceiveMessageRequest failed: ", err) + continue + } + + if output == nil || len(output.Messages) == 0 { + c.logger.Debug("no message received from SQS") + continue + } + + // process messages received from sqs, get logs from s3 and create events + c.processor(c.config.QueueURL, output.Messages, c.visibilityTimeout, c.s3, c.sqs) + } +} + +func (c *s3Collector) processor(queueURL string, messages []sqs.Message, visibilityTimeout int64, svcS3 s3iface.ClientAPI, svcSQS sqsiface.ClientAPI) { + var grp unison.MultiErrGroup + numMessages := len(messages) + c.logger.Debugf("Processing %v messages", numMessages) + + // process messages received from sqs + for i := range messages { + i := i + errC := make(chan error) + grp.Go(func() (err error) { + return c.processMessage(svcS3, messages[i], errC) + }) + grp.Go(func() (err error) { + return c.processorKeepAlive(svcSQS, messages[i], queueURL, visibilityTimeout, errC) + }) + } + grp.Wait() +} + +func (c *s3Collector) processMessage(svcS3 s3iface.ClientAPI, message sqs.Message, errC chan error) error { + s3Infos, err := c.handleSQSMessage(message) + if err != nil { + c.logger.Error(fmt.Errorf("handleSQSMessage failed: %w", err)) + return err + } + c.logger.Debugf("handleSQSMessage succeed and returned %v sets of S3 log info", len(s3Infos)) + + // read from s3 object and create event for each log line + err = c.handleS3Objects(svcS3, s3Infos, errC) + if err != nil { + err = fmt.Errorf("handleS3Objects failed: %w", err) + c.logger.Error(err) + return err + } + c.logger.Debugf("handleS3Objects succeed") + return nil +} + +func (c *s3Collector) processorKeepAlive(svcSQS sqsiface.ClientAPI, message sqs.Message, queueURL string, visibilityTimeout int64, errC chan error) error { + for { + select { + case <-c.cancellation.Done(): + return nil + case err := <-errC: + if err != nil { + c.logger.Warn("Processing message failed, updating visibility timeout") + err := c.changeVisibilityTimeout(queueURL, visibilityTimeout, svcSQS, message.ReceiptHandle) + if err != nil { + c.logger.Error(fmt.Errorf("SQS ChangeMessageVisibilityRequest failed: %w", err)) + } + c.logger.Infof("Message visibility timeout updated to %v", visibilityTimeout) + } else { + // When ACK done, message will be deleted. Or when message is + // not s3 ObjectCreated event related(handleSQSMessage function + // failed), it will be removed as well. + c.logger.Debug("Deleting message from SQS: ", *message.MessageId) + // only delete sqs message when errC is closed with no error + err := c.deleteMessage(queueURL, *message.ReceiptHandle, svcSQS) + if err != nil { + c.logger.Error(fmt.Errorf("deleteMessages failed: %w", err)) + } + } + return err + case <-time.After(time.Duration(visibilityTimeout/2) * time.Second): + c.logger.Warn("Half of the set visibilityTimeout passed, visibility timeout needs to be updated") + // If half of the set visibilityTimeout passed and this is + // still ongoing, then change visibility timeout. + err := c.changeVisibilityTimeout(queueURL, visibilityTimeout, svcSQS, message.ReceiptHandle) + if err != nil { + c.logger.Error(fmt.Errorf("SQS ChangeMessageVisibilityRequest failed: %w", err)) + } + c.logger.Infof("Message visibility timeout updated to %v seconds", visibilityTimeout) + return err + } + } +} + +func (c *s3Collector) receiveMessage(svcSQS sqsiface.ClientAPI, visibilityTimeout int64) (*sqs.ReceiveMessageResponse, error) { + // receive messages from sqs + req := svcSQS.ReceiveMessageRequest( + &sqs.ReceiveMessageInput{ + QueueUrl: &c.config.QueueURL, + MessageAttributeNames: []string{"All"}, + MaxNumberOfMessages: awssdk.Int64(int64(maxNumberOfMessage)), + VisibilityTimeout: &visibilityTimeout, + WaitTimeSeconds: awssdk.Int64(int64(waitTimeSecond)), + }) + + // The Context will interrupt the request if the timeout expires. + sendCtx, cancelFn := context.WithTimeout(c.cancellation, c.config.APITimeout) + defer cancelFn() + + return req.Send(sendCtx) +} + +func (c *s3Collector) changeVisibilityTimeout(queueURL string, visibilityTimeout int64, svcSQS sqsiface.ClientAPI, receiptHandle *string) error { + req := svcSQS.ChangeMessageVisibilityRequest(&sqs.ChangeMessageVisibilityInput{ + QueueUrl: &queueURL, + VisibilityTimeout: &visibilityTimeout, + ReceiptHandle: receiptHandle, + }) + + // The Context will interrupt the request if the timeout expires. + sendCtx, cancelFn := context.WithTimeout(c.cancellation, c.config.APITimeout) + defer cancelFn() + + _, err := req.Send(sendCtx) + return err +} + +func getRegionFromQueueURL(queueURL string) (string, error) { + // get region from queueURL + // Example: https://sqs.us-east-1.amazonaws.com/627959692251/test-s3-logs + queueURLSplit := strings.Split(queueURL, ".") + if queueURLSplit[0] == "https://sqs" && queueURLSplit[2] == "amazonaws" { + return queueURLSplit[1], nil + } + return "", fmt.Errorf("queueURL is not in format: https://sqs.{REGION_ENDPOINT}.amazonaws.com/{ACCOUNT_NUMBER}/{QUEUE_NAME}") +} + +// handle message +func (c *s3Collector) handleSQSMessage(m sqs.Message) ([]s3Info, error) { + msg := sqsMessage{} + err := json.Unmarshal([]byte(*m.Body), &msg) + if err != nil { + return nil, fmt.Errorf("json unmarshal sqs message body failed: %w", err) + } + + var s3Infos []s3Info + for _, record := range msg.Records { + if record.EventSource != "aws:s3" || !strings.HasPrefix(record.EventName, "ObjectCreated:") { + return nil, fmt.Errorf("this SQS queue should be dedicated to s3 ObjectCreated event notifications") + } + // Unescape substrings from s3 log name. For example, convert "%3D" back to "=" + filename, err := url.QueryUnescape(record.S3.object.Key) + if err != nil { + return nil, fmt.Errorf("url.QueryUnescape failed for '%s': %w", record.S3.object.Key, err) + } + + if len(c.config.FileSelectors) == 0 { + s3Infos = append(s3Infos, s3Info{ + region: record.AwsRegion, + name: record.S3.bucket.Name, + key: filename, + arn: record.S3.bucket.Arn, + expandEventListFromField: c.config.ExpandEventListFromField, + }) + continue + } + + for _, fs := range c.config.FileSelectors { + if fs.Regex == nil { + continue + } + if fs.Regex.MatchString(filename) { + s3Infos = append(s3Infos, s3Info{ + region: record.AwsRegion, + name: record.S3.bucket.Name, + key: filename, + arn: record.S3.bucket.Arn, + expandEventListFromField: fs.ExpandEventListFromField, + }) + break + } + } + } + return s3Infos, nil +} + +func (c *s3Collector) handleS3Objects(svc s3iface.ClientAPI, s3Infos []s3Info, errC chan error) error { + s3Ctx := &s3Context{ + refs: 1, + errC: errC, + } + defer s3Ctx.done() + + for _, info := range s3Infos { + c.logger.Debugf("Processing file from s3 bucket \"%s\" with name \"%s\"", info.name, info.key) + err := c.createEventsFromS3Info(svc, info, s3Ctx) + if err != nil { + err = fmt.Errorf("createEventsFromS3Info failed processing file from s3 bucket \"%s\" with name \"%s\": %w", info.name, info.key, err) + c.logger.Error(err) + s3Ctx.setError(err) + } + } + return nil +} + +func (c *s3Collector) createEventsFromS3Info(svc s3iface.ClientAPI, info s3Info, s3Ctx *s3Context) error { + objectHash := s3ObjectHash(info) + + // Download the S3 object using GetObjectRequest. + s3GetObjectInput := &s3.GetObjectInput{ + Bucket: awssdk.String(info.name), + Key: awssdk.String(info.key), + } + req := svc.GetObjectRequest(s3GetObjectInput) + + // The Context will interrupt the request if the timeout expires. + ctx, cancelFn := context.WithTimeout(c.cancellation, c.config.APITimeout) + defer cancelFn() + + resp, err := req.Send(ctx) + if err != nil { + if awsErr, ok := err.(awserr.Error); ok { + // If the SDK can determine the request or retry delay was canceled + // by a context the ErrCodeRequestCanceled error will be returned. + if awsErr.Code() == awssdk.ErrCodeRequestCanceled { + err = fmt.Errorf("s3 GetObjectRequest canceled for '%s' from S3 bucket '%s': %w", info.key, info.name, err) + c.logger.Error(err) + return err + } + + if awsErr.Code() == "NoSuchKey" { + c.logger.Warnf("Cannot find s3 file '%s' from S3 bucket '%s'", info.key, info.name) + return nil + } + } + return fmt.Errorf("s3 GetObjectRequest failed for '%s' from S3 bucket '%s': %w", info.key, info.name, err) + } + + defer resp.Body.Close() + + reader := bufio.NewReader(resp.Body) + + isS3ObjGzipped, err := isStreamGzipped(reader) + if err != nil { + err = fmt.Errorf("could not determine if S3 object is gzipped: %w", err) + c.logger.Error(err) + return err + } + + if isS3ObjGzipped { + gzipReader, err := gzip.NewReader(reader) + if err != nil { + err = fmt.Errorf("gzip.NewReader failed for '%s' from S3 bucket '%s': %w", info.key, info.name, err) + c.logger.Error(err) + return err + } + reader = bufio.NewReader(gzipReader) + gzipReader.Close() + } + + // Decode JSON documents when content-type is "application/json" or expand_event_list_from_field is given in config + if resp.ContentType != nil && *resp.ContentType == "application/json" || info.expandEventListFromField != "" { + decoder := json.NewDecoder(reader) + err := c.decodeJSON(decoder, objectHash, info, s3Ctx) + if err != nil { + err = fmt.Errorf("decodeJSONWithKey failed for '%s' from S3 bucket '%s': %w", info.key, info.name, err) + c.logger.Error(err) + return err + } + return nil + } + + // handle s3 objects that are not json content-type + offset := 0 + for { + log, err := readStringAndTrimDelimiter(reader) + if err == io.EOF { + // create event for last line + offset += len([]byte(log)) + event := createEvent(log, offset, info, objectHash, s3Ctx) + err = c.forwardEvent(event) + if err != nil { + err = fmt.Errorf("forwardEvent failed: %w", err) + c.logger.Error(err) + return err + } + return nil + } else if err != nil { + err = fmt.Errorf("readStringAndTrimDelimiter failed: %w", err) + c.logger.Error(err) + return err + } + + if log == "" { + break + } + + // create event per log line + offset += len([]byte(log)) + event := createEvent(log, offset, info, objectHash, s3Ctx) + err = c.forwardEvent(event) + if err != nil { + err = fmt.Errorf("forwardEvent failed: %w", err) + c.logger.Error(err) + return err + } + } + return nil +} + +func (c *s3Collector) decodeJSON(decoder *json.Decoder, objectHash string, s3Info s3Info, s3Ctx *s3Context) error { + offset := 0 + for { + var jsonFields interface{} + err := decoder.Decode(&jsonFields) + if jsonFields == nil { + return nil + } + + if err == io.EOF { + offsetNew, err := c.jsonFieldsType(jsonFields, offset, objectHash, s3Info, s3Ctx) + if err != nil { + return err + } + offset = offsetNew + } else if err != nil { + // decode json failed, skip this log file + err = fmt.Errorf("decode json failed for '%s' from S3 bucket '%s', skipping this file: %w", s3Info.key, s3Info.name, err) + c.logger.Warn(err) + return nil + } + + offset, err = c.jsonFieldsType(jsonFields, offset, objectHash, s3Info, s3Ctx) + if err != nil { + return err + } + } +} + +func (c *s3Collector) jsonFieldsType(jsonFields interface{}, offset int, objectHash string, s3Info s3Info, s3Ctx *s3Context) (int, error) { + switch f := jsonFields.(type) { + case map[string][]interface{}: + if s3Info.expandEventListFromField != "" { + textValues, ok := f[s3Info.expandEventListFromField] + if !ok { + err := fmt.Errorf("key '%s' not found", s3Info.expandEventListFromField) + c.logger.Error(err) + return offset, err + } + for _, v := range textValues { + offset, err := c.convertJSONToEvent(v, offset, objectHash, s3Info, s3Ctx) + if err != nil { + err = fmt.Errorf("convertJSONToEvent failed for '%s' from S3 bucket '%s': %w", s3Info.key, s3Info.name, err) + c.logger.Error(err) + return offset, err + } + } + return offset, nil + } + case map[string]interface{}: + if s3Info.expandEventListFromField != "" { + textValues, ok := f[s3Info.expandEventListFromField] + if !ok { + err := fmt.Errorf("key '%s' not found", s3Info.expandEventListFromField) + c.logger.Error(err) + return offset, err + } + + valuesConverted := textValues.([]interface{}) + for _, textValue := range valuesConverted { + offsetNew, err := c.convertJSONToEvent(textValue, offset, objectHash, s3Info, s3Ctx) + if err != nil { + err = fmt.Errorf("convertJSONToEvent failed for '%s' from S3 bucket '%s': %w", s3Info.key, s3Info.name, err) + c.logger.Error(err) + return offset, err + } + offset = offsetNew + } + return offset, nil + } + + offset, err := c.convertJSONToEvent(f, offset, objectHash, s3Info, s3Ctx) + if err != nil { + err = fmt.Errorf("convertJSONToEvent failed for '%s' from S3 bucket '%s': %w", s3Info.key, s3Info.name, err) + c.logger.Error(err) + return offset, err + } + return offset, nil + } + return offset, nil +} + +func (c *s3Collector) convertJSONToEvent(jsonFields interface{}, offset int, objectHash string, s3Info s3Info, s3Ctx *s3Context) (int, error) { + vJSON, _ := json.Marshal(jsonFields) + logOriginal := string(vJSON) + log := trimLogDelimiter(logOriginal) + offset += len([]byte(log)) + event := createEvent(log, offset, s3Info, objectHash, s3Ctx) + + err := c.forwardEvent(event) + if err != nil { + err = fmt.Errorf("forwardEvent failed: %w", err) + c.logger.Error(err) + return offset, err + } + return offset, nil +} + +func (c *s3Collector) forwardEvent(event beat.Event) error { + c.publisher.Publish(event) + return c.cancellation.Err() +} + +func (c *s3Collector) deleteMessage(queueURL string, messagesReceiptHandle string, svcSQS sqsiface.ClientAPI) error { + deleteMessageInput := &sqs.DeleteMessageInput{ + QueueUrl: awssdk.String(queueURL), + ReceiptHandle: awssdk.String(messagesReceiptHandle), + } + + req := svcSQS.DeleteMessageRequest(deleteMessageInput) + + // The Context will interrupt the request if the timeout expires. + ctx, cancelFn := context.WithTimeout(c.cancellation, c.config.APITimeout) + defer cancelFn() + + _, err := req.Send(ctx) + if err != nil { + if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == awssdk.ErrCodeRequestCanceled { + return nil + } + return fmt.Errorf("SQS DeleteMessageRequest failed: %w", err) + } + return nil +} + +func trimLogDelimiter(log string) string { + return strings.TrimSuffix(log, "\n") +} + +func readStringAndTrimDelimiter(reader *bufio.Reader) (string, error) { + logOriginal, err := reader.ReadString('\n') + if err != nil { + return logOriginal, err + } + return trimLogDelimiter(logOriginal), nil +} + +func createEvent(log string, offset int, info s3Info, objectHash string, s3Ctx *s3Context) beat.Event { + s3Ctx.Inc() + + event := beat.Event{ + Timestamp: time.Now().UTC(), + Fields: common.MapStr{ + "message": log, + "log": common.MapStr{ + "offset": int64(offset), + "file.path": constructObjectURL(info), + }, + "aws": common.MapStr{ + "s3": common.MapStr{ + "bucket": common.MapStr{ + "name": info.name, + "arn": info.arn}, + "object.key": info.key, + }, + }, + "cloud": common.MapStr{ + "provider": "aws", + "region": info.region, + }, + }, + Private: s3Ctx, + } + event.SetID(objectHash + "-" + fmt.Sprintf("%012d", offset)) + + return event +} + +func constructObjectURL(info s3Info) string { + return "https://" + info.name + ".s3-" + info.region + ".amazonaws.com/" + info.key +} + +// s3ObjectHash returns a short sha256 hash of the bucket arn + object key name. +func s3ObjectHash(s3Info s3Info) string { + h := sha256.New() + h.Write([]byte(s3Info.arn + s3Info.key)) + prefix := hex.EncodeToString(h.Sum(nil)) + return prefix[:10] +} + +func (c *s3Context) setError(err error) { + // only care about the last error for now + // TODO: add "Typed" error to error for context + c.mux.Lock() + defer c.mux.Unlock() + c.err = err +} + +func (c *s3Context) done() { + c.mux.Lock() + defer c.mux.Unlock() + c.refs-- + if c.refs == 0 { + c.errC <- c.err + close(c.errC) + } +} + +func (c *s3Context) Inc() { + c.mux.Lock() + defer c.mux.Unlock() + c.refs++ +} + +// isStreamGzipped determines whether the given stream of bytes (encapsulated in a buffered reader) +// represents gzipped content or not. A buffered reader is used so the function can peek into the byte +// stream without consuming it. This makes it convenient for code executed after this function call +// to consume the stream if it wants. +func isStreamGzipped(r *bufio.Reader) (bool, error) { + // Why 512? See https://godoc.org/net/http#DetectContentType + buf, err := r.Peek(512) + if err != nil && err != io.EOF { + return false, err + } + + switch http.DetectContentType(buf) { + case "application/x-gzip", "application/zip": + return true, nil + default: + return false, nil + } +} diff --git a/x-pack/filebeat/input/s3/input_test.go b/x-pack/filebeat/input/s3/collector_test.go similarity index 97% rename from x-pack/filebeat/input/s3/input_test.go rename to x-pack/filebeat/input/s3/collector_test.go index d1fab05cb3c..510f94d40d5 100644 --- a/x-pack/filebeat/input/s3/input_test.go +++ b/x-pack/filebeat/input/s3/collector_test.go @@ -120,7 +120,7 @@ func TestHandleMessage(t *testing.T) { }, } - p := &s3Input{context: &channelContext{}} + p := &s3Collector{config: &config{}} for _, c := range casesPositive { t.Run(c.title, func(t *testing.T) { s3Info, err := p.handleSQSMessage(c.message) @@ -165,7 +165,8 @@ func TestHandleMessage(t *testing.T) { } func TestNewS3BucketReader(t *testing.T) { - p := &s3Input{context: &channelContext{}} + config := defaultConfig() + p := &s3Collector{cancellation: context.TODO(), config: &config} s3GetObjectInput := &s3.GetObjectInput{ Bucket: awssdk.String(info.name), Key: awssdk.String(info.key), @@ -174,7 +175,7 @@ func TestNewS3BucketReader(t *testing.T) { // The Context will interrupt the request if the timeout expires. var cancelFn func() - ctx, cancelFn := context.WithTimeout(p.context, p.config.APITimeout) + ctx, cancelFn := context.WithTimeout(p.cancellation, p.config.APITimeout) defer cancelFn() resp, err := req.Send(ctx) @@ -201,7 +202,8 @@ func TestNewS3BucketReader(t *testing.T) { } func TestCreateEvent(t *testing.T) { - p := &s3Input{context: &channelContext{}} + config := defaultConfig() + p := &s3Collector{cancellation: context.TODO(), config: &config} errC := make(chan error) s3Context := &s3Context{ refs: 1, @@ -225,7 +227,7 @@ func TestCreateEvent(t *testing.T) { // The Context will interrupt the request if the timeout expires. var cancelFn func() - ctx, cancelFn := context.WithTimeout(p.context, p.config.APITimeout) + ctx, cancelFn := context.WithTimeout(p.cancellation, p.config.APITimeout) defer cancelFn() resp, err := req.Send(ctx) diff --git a/x-pack/filebeat/input/s3/config.go b/x-pack/filebeat/input/s3/config.go index f9780d82277..5f37a436d12 100644 --- a/x-pack/filebeat/input/s3/config.go +++ b/x-pack/filebeat/input/s3/config.go @@ -9,18 +9,16 @@ import ( "regexp" "time" - "github.com/elastic/beats/v7/filebeat/harvester" awscommon "github.com/elastic/beats/v7/x-pack/libbeat/common/aws" ) type config struct { - harvester.ForwarderConfig `config:",inline"` - QueueURL string `config:"queue_url" validate:"nonzero,required"` - VisibilityTimeout time.Duration `config:"visibility_timeout"` - AwsConfig awscommon.ConfigAWS `config:",inline"` - ExpandEventListFromField string `config:"expand_event_list_from_field"` - APITimeout time.Duration `config:"api_timeout"` - FileSelectors []FileSelectorCfg `config:"file_selectors"` + QueueURL string `config:"queue_url" validate:"nonzero,required"` + VisibilityTimeout time.Duration `config:"visibility_timeout"` + AwsConfig awscommon.ConfigAWS `config:",inline"` + ExpandEventListFromField string `config:"expand_event_list_from_field"` + APITimeout time.Duration `config:"api_timeout"` + FileSelectors []FileSelectorCfg `config:"file_selectors"` } // FileSelectorCfg defines type and configuration of FileSelectors @@ -32,9 +30,6 @@ type FileSelectorCfg struct { func defaultConfig() config { return config{ - ForwarderConfig: harvester.ForwarderConfig{ - Type: "s3", - }, VisibilityTimeout: 300 * time.Second, APITimeout: 120 * time.Second, } diff --git a/x-pack/filebeat/input/s3/input.go b/x-pack/filebeat/input/s3/input.go index 83dc48428ee..a6b56d03970 100644 --- a/x-pack/filebeat/input/s3/input.go +++ b/x-pack/filebeat/input/s3/input.go @@ -5,748 +5,120 @@ package s3 import ( - "bufio" - "compress/gzip" - "context" - "crypto/sha256" - "encoding/hex" - "encoding/json" "fmt" - "io" - "net/http" - "net/url" - "strings" - "sync" - "time" - awssdk "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/aws/awserr" "github.com/aws/aws-sdk-go-v2/service/s3" - "github.com/aws/aws-sdk-go-v2/service/s3/s3iface" "github.com/aws/aws-sdk-go-v2/service/sqs" - "github.com/aws/aws-sdk-go-v2/service/sqs/sqsiface" - "github.com/pkg/errors" - "github.com/elastic/beats/v7/filebeat/channel" - "github.com/elastic/beats/v7/filebeat/input" + v2 "github.com/elastic/beats/v7/filebeat/input/v2" "github.com/elastic/beats/v7/libbeat/beat" "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/common/acker" - "github.com/elastic/beats/v7/libbeat/common/cfgwarn" - "github.com/elastic/beats/v7/libbeat/logp" + "github.com/elastic/beats/v7/libbeat/feature" awscommon "github.com/elastic/beats/v7/x-pack/libbeat/common/aws" + "github.com/elastic/go-concert/ctxtool" ) const inputName = "s3" -var ( - // The maximum number of messages to return. Amazon SQS never returns more messages - // than this value (however, fewer messages might be returned). - maxNumberOfMessage int64 = 10 - - // The duration (in seconds) for which the call waits for a message to arrive - // in the queue before returning. If a message is available, the call returns - // sooner than WaitTimeSeconds. If no messages are available and the wait time - // expires, the call returns successfully with an empty list of messages. - waitTimeSecond int64 = 10 - - errOutletClosed = errors.New("input outlet closed") -) - -func init() { - err := input.Register(inputName, NewInput) - if err != nil { - panic(err) +func Plugin() v2.Plugin { + return v2.Plugin{ + Name: inputName, + Stability: feature.Beta, + Deprecated: false, + Info: "Collect logs from s3", + Manager: v2.ConfigureWith(configure), } } -// s3Input is a input for s3 -type s3Input struct { - outlet channel.Outleter // Output of received s3 logs. - config config - awsConfig awssdk.Config - logger *logp.Logger - close chan struct{} - workerOnce sync.Once // Guarantees that the worker goroutine is only started once. - context *channelContext - workerWg sync.WaitGroup // Waits on s3 worker goroutine. - stopOnce sync.Once -} - -type s3Info struct { - name string - key string - region string - arn string - expandEventListFromField string -} - -type bucket struct { - Name string `json:"name"` - Arn string `json:"arn"` -} - -type object struct { - Key string `json:"key"` -} - -type s3BucketObject struct { - bucket `json:"bucket"` - object `json:"object"` -} - -type sqsMessage struct { - Records []struct { - EventSource string `json:"eventSource"` - AwsRegion string `json:"awsRegion"` - EventName string `json:"eventName"` - S3 s3BucketObject `json:"s3"` - } `json:"Records"` -} - -type s3Context struct { - mux sync.Mutex - refs int - err error // first error witnessed or multi error - errC chan error -} - -// channelContext implements context.Context by wrapping a channel -type channelContext struct { - done <-chan struct{} -} - -func (c *channelContext) Deadline() (time.Time, bool) { return time.Time{}, false } -func (c *channelContext) Done() <-chan struct{} { return c.done } -func (c *channelContext) Err() error { - select { - case <-c.done: - return context.Canceled - default: - return nil - } -} -func (c *channelContext) Value(key interface{}) interface{} { return nil } - -// NewInput creates a new s3 input -func NewInput(cfg *common.Config, connector channel.Connector, context input.Context) (input.Input, error) { - cfgwarn.Beta("s3 input type is used") - logger := logp.NewLogger(inputName) - +func configure(cfg *common.Config) (v2.Input, error) { config := defaultConfig() if err := cfg.Unpack(&config); err != nil { - return nil, errors.Wrap(err, "failed unpacking config") - } - - out, err := connector.ConnectWith(cfg, beat.ClientConfig{ - ACKHandler: acker.ConnectionOnly( - acker.EventPrivateReporter(func(_ int, privates []interface{}) { - for _, private := range privates { - if s3Context, ok := private.(*s3Context); ok { - s3Context.done() - } - } - }), - ), - }) - if err != nil { return nil, err } - awsConfig, err := awscommon.GetAWSCredentials(config.AwsConfig) - if err != nil { - return nil, errors.Wrap(err, "getAWSCredentials failed") - } - - closeChannel := make(chan struct{}) - p := &s3Input{ - outlet: out, - config: config, - awsConfig: awsConfig, - logger: logger, - close: closeChannel, - context: &channelContext{closeChannel}, - } - return p, nil -} - -// Run runs the input -func (p *s3Input) Run() { - p.workerOnce.Do(func() { - visibilityTimeout := int64(p.config.VisibilityTimeout.Seconds()) - p.logger.Infof("visibility timeout is set to %v seconds", visibilityTimeout) - p.logger.Infof("aws api timeout is set to %v", p.config.APITimeout) - - regionName, err := getRegionFromQueueURL(p.config.QueueURL) - if err != nil { - p.logger.Errorf("failed to get region name from queueURL: %v", p.config.QueueURL) - } - - awsConfig := p.awsConfig.Copy() - awsConfig.Region = regionName - - svcSQS := sqs.New(awscommon.EnrichAWSConfigWithEndpoint(p.config.AwsConfig.Endpoint, "sqs", regionName, awsConfig)) - svcS3 := s3.New(awscommon.EnrichAWSConfigWithEndpoint(p.config.AwsConfig.Endpoint, "s3", regionName, awsConfig)) - - p.workerWg.Add(1) - go p.run(svcSQS, svcS3, visibilityTimeout) - }) -} - -func (p *s3Input) run(svcSQS sqsiface.ClientAPI, svcS3 s3iface.ClientAPI, visibilityTimeout int64) { - defer p.workerWg.Done() - defer p.logger.Infof("s3 input worker for '%v' has stopped.", p.config.QueueURL) - - p.logger.Infof("s3 input worker has started. with queueURL: %v", p.config.QueueURL) - for p.context.Err() == nil { - // receive messages from sqs - output, err := p.receiveMessage(svcSQS, visibilityTimeout) - if err != nil { - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == awssdk.ErrCodeRequestCanceled { - continue - } - p.logger.Error("SQS ReceiveMessageRequest failed: ", err) - time.Sleep(time.Duration(waitTimeSecond) * time.Second) - continue - } - - if output == nil || len(output.Messages) == 0 { - p.logger.Debug("no message received from SQS:", p.config.QueueURL) - continue - } - - // process messages received from sqs, get logs from s3 and create events - p.processor(p.config.QueueURL, output.Messages, visibilityTimeout, svcS3, svcSQS) - } -} - -// Stop stops the s3 input -func (p *s3Input) Stop() { - p.stopOnce.Do(func() { - defer p.outlet.Close() - close(p.close) - p.logger.Info("Stopping s3 input") - }) -} - -// Wait stops the s3 input. -func (p *s3Input) Wait() { - p.Stop() - p.workerWg.Wait() -} - -func (p *s3Input) processor(queueURL string, messages []sqs.Message, visibilityTimeout int64, svcS3 s3iface.ClientAPI, svcSQS sqsiface.ClientAPI) { - var wg sync.WaitGroup - numMessages := len(messages) - p.logger.Debugf("Processing %v messages", numMessages) - wg.Add(numMessages * 2) - - // process messages received from sqs - for i := range messages { - errC := make(chan error) - go p.processMessage(svcS3, messages[i], &wg, errC) - go p.processorKeepAlive(svcSQS, messages[i], queueURL, visibilityTimeout, &wg, errC) - } - wg.Wait() + return newInput(config) } -func (p *s3Input) processMessage(svcS3 s3iface.ClientAPI, message sqs.Message, wg *sync.WaitGroup, errC chan error) { - defer wg.Done() - - s3Infos, err := p.handleSQSMessage(message) - if err != nil { - p.logger.Error(errors.Wrap(err, "handleSQSMessage failed")) - return - } - p.logger.Debugf("handleSQSMessage succeed and returned %v sets of S3 log info", len(s3Infos)) - - // read from s3 object and create event for each log line - err = p.handleS3Objects(svcS3, s3Infos, errC) - if err != nil { - err = errors.Wrap(err, "handleS3Objects failed") - p.logger.Error(err) - return - } - p.logger.Debugf("handleS3Objects succeed") -} - -func (p *s3Input) processorKeepAlive(svcSQS sqsiface.ClientAPI, message sqs.Message, queueURL string, visibilityTimeout int64, wg *sync.WaitGroup, errC chan error) { - defer wg.Done() - for { - select { - case <-p.close: - return - case err := <-errC: - if err != nil { - p.logger.Warn("Processing message failed, updating visibility timeout") - err := p.changeVisibilityTimeout(queueURL, visibilityTimeout, svcSQS, message.ReceiptHandle) - if err != nil { - p.logger.Error(errors.Wrap(err, "SQS ChangeMessageVisibilityRequest failed")) - } - p.logger.Infof("Message visibility timeout updated to %v", visibilityTimeout) - } else { - // When ACK done, message will be deleted. Or when message is - // not s3 ObjectCreated event related(handleSQSMessage function - // failed), it will be removed as well. - p.logger.Debug("Deleting message from SQS: ", *message.MessageId) - // only delete sqs message when errC is closed with no error - err := p.deleteMessage(queueURL, *message.ReceiptHandle, svcSQS) - if err != nil { - p.logger.Error(errors.Wrap(err, "deleteMessages failed")) - } - } - return - case <-time.After(time.Duration(visibilityTimeout/2) * time.Second): - p.logger.Warn("Half of the set visibilityTimeout passed, visibility timeout needs to be updated") - // If half of the set visibilityTimeout passed and this is - // still ongoing, then change visibility timeout. - err := p.changeVisibilityTimeout(queueURL, visibilityTimeout, svcSQS, message.ReceiptHandle) - if err != nil { - p.logger.Error(errors.Wrap(err, "SQS ChangeMessageVisibilityRequest failed")) - } - p.logger.Infof("Message visibility timeout updated to %v seconds", visibilityTimeout) - } - } -} - -func (p *s3Input) receiveMessage(svcSQS sqsiface.ClientAPI, visibilityTimeout int64) (*sqs.ReceiveMessageResponse, error) { - // receive messages from sqs - req := svcSQS.ReceiveMessageRequest( - &sqs.ReceiveMessageInput{ - QueueUrl: &p.config.QueueURL, - MessageAttributeNames: []string{"All"}, - MaxNumberOfMessages: &maxNumberOfMessage, - VisibilityTimeout: &visibilityTimeout, - WaitTimeSeconds: &waitTimeSecond, - }) - - // The Context will interrupt the request if the timeout expires. - ctx, cancelFn := context.WithTimeout(p.context, p.config.APITimeout) - defer cancelFn() - - return req.Send(ctx) +// s3Input is a input for s3 +type s3Input struct { + config config } -func (p *s3Input) changeVisibilityTimeout(queueURL string, visibilityTimeout int64, svcSQS sqsiface.ClientAPI, receiptHandle *string) error { - req := svcSQS.ChangeMessageVisibilityRequest(&sqs.ChangeMessageVisibilityInput{ - QueueUrl: &queueURL, - VisibilityTimeout: &visibilityTimeout, - ReceiptHandle: receiptHandle, - }) - - // The Context will interrupt the request if the timeout expires. - ctx, cancelFn := context.WithTimeout(p.context, p.config.APITimeout) - defer cancelFn() - - _, err := req.Send(ctx) - return err +func newInput(config config) (*s3Input, error) { + return &s3Input{config: config}, nil } -func getRegionFromQueueURL(queueURL string) (string, error) { - // get region from queueURL - // Example: https://sqs.us-east-1.amazonaws.com/627959692251/test-s3-logs - queueURLSplit := strings.Split(queueURL, ".") - if queueURLSplit[0] == "https://sqs" && queueURLSplit[2] == "amazonaws" { - return queueURLSplit[1], nil - } - return "", errors.New("queueURL is not in format: https://sqs.{REGION_ENDPOINT}.amazonaws.com/{ACCOUNT_NUMBER}/{QUEUE_NAME}") -} +func (in *s3Input) Name() string { return inputName } -// handle message -func (p *s3Input) handleSQSMessage(m sqs.Message) ([]s3Info, error) { - msg := sqsMessage{} - err := json.Unmarshal([]byte(*m.Body), &msg) +func (in *s3Input) Test(ctx v2.TestContext) error { + _, err := awscommon.GetAWSCredentials(in.config.AwsConfig) if err != nil { - return nil, errors.Wrap(err, "json unmarshal sqs message body failed") - } - - var s3Infos []s3Info - for _, record := range msg.Records { - if record.EventSource != "aws:s3" || !strings.HasPrefix(record.EventName, "ObjectCreated:") { - return nil, errors.New("this SQS queue should be dedicated to s3 ObjectCreated event notifications") - } - // Unescape substrings from s3 log name. For example, convert "%3D" back to "=" - filename, err := url.QueryUnescape(record.S3.object.Key) - if err != nil { - return nil, errors.Wrapf(err, "url.QueryUnescape failed for '%s'", record.S3.object.Key) - } - - if len(p.config.FileSelectors) == 0 { - s3Infos = append(s3Infos, s3Info{ - region: record.AwsRegion, - name: record.S3.bucket.Name, - key: filename, - arn: record.S3.bucket.Arn, - expandEventListFromField: p.config.ExpandEventListFromField, - }) - continue - } - - for _, fs := range p.config.FileSelectors { - if fs.Regex == nil { - continue - } - if fs.Regex.MatchString(filename) { - s3Infos = append(s3Infos, s3Info{ - region: record.AwsRegion, - name: record.S3.bucket.Name, - key: filename, - arn: record.S3.bucket.Arn, - expandEventListFromField: fs.ExpandEventListFromField, - }) - break - } - } - } - return s3Infos, nil -} - -func (p *s3Input) handleS3Objects(svc s3iface.ClientAPI, s3Infos []s3Info, errC chan error) error { - s3Ctx := &s3Context{ - refs: 1, - errC: errC, - } - defer s3Ctx.done() - - for _, info := range s3Infos { - p.logger.Debugf("Processing file from s3 bucket \"%s\" with name \"%s\"", info.name, info.key) - err := p.createEventsFromS3Info(svc, info, s3Ctx) - if err != nil { - err = errors.Wrapf(err, "createEventsFromS3Info failed processing file from s3 bucket \"%s\" with name \"%s\"", info.name, info.key) - p.logger.Error(err) - s3Ctx.setError(err) - } + return fmt.Errorf("getAWSCredentials failed: %w", err) } return nil } -func (p *s3Input) createEventsFromS3Info(svc s3iface.ClientAPI, info s3Info, s3Ctx *s3Context) error { - objectHash := s3ObjectHash(info) - - // Download the S3 object using GetObjectRequest. - s3GetObjectInput := &s3.GetObjectInput{ - Bucket: awssdk.String(info.name), - Key: awssdk.String(info.key), - } - req := svc.GetObjectRequest(s3GetObjectInput) - - // The Context will interrupt the request if the timeout expires. - ctx, cancelFn := context.WithTimeout(p.context, p.config.APITimeout) - defer cancelFn() - - resp, err := req.Send(ctx) +func (in *s3Input) Run(ctx v2.Context, pipeline beat.Pipeline) error { + collector, err := in.createCollector(ctx, pipeline) if err != nil { - if awsErr, ok := err.(awserr.Error); ok { - // If the SDK can determine the request or retry delay was canceled - // by a context the ErrCodeRequestCanceled error will be returned. - if awsErr.Code() == awssdk.ErrCodeRequestCanceled { - err = errors.Wrapf(err, "S3 GetObjectRequest canceled for '%s' from S3 bucket '%s'", info.key, info.name) - p.logger.Error(err) - return err - } - - if awsErr.Code() == "NoSuchKey" { - p.logger.Warnf("Cannot find s3 file '%s' from S3 bucket '%s'", info.key, info.name) - return nil - } - } - return errors.Wrapf(err, "S3 GetObjectRequest failed for '%s' from S3 bucket '%s'", info.key, info.name) - } - - defer resp.Body.Close() - - reader := bufio.NewReader(resp.Body) - - isS3ObjGzipped, err := isStreamGzipped(reader) - if err != nil { - err = errors.Wrap(err, "could not determine if S3 object is gzipped") - p.logger.Error(err) return err } - if isS3ObjGzipped { - gzipReader, err := gzip.NewReader(reader) - if err != nil { - err = errors.Wrapf(err, "gzip.NewReader failed for '%s' from S3 bucket '%s'", info.key, info.name) - p.logger.Error(err) - return err - } - reader = bufio.NewReader(gzipReader) - gzipReader.Close() - } - - // Decode JSON documents when content-type is "application/json" or expand_event_list_from_field is given in config - if resp.ContentType != nil && *resp.ContentType == "application/json" || info.expandEventListFromField != "" { - decoder := json.NewDecoder(reader) - err := p.decodeJSON(decoder, objectHash, info, s3Ctx) - if err != nil { - err = errors.Wrapf(err, "decodeJSONWithKey failed for '%s' from S3 bucket '%s'", info.key, info.name) - p.logger.Error(err) - return err - } - return nil - } - - // handle s3 objects that are not json content-type - offset := 0 - for { - log, err := readStringAndTrimDelimiter(reader) - if err == io.EOF { - // create event for last line - offset += len([]byte(log)) - event := createEvent(log, offset, info, objectHash, s3Ctx) - err = p.forwardEvent(event) - if err != nil { - err = errors.Wrap(err, "forwardEvent failed") - p.logger.Error(err) - return err - } - return nil - } else if err != nil { - err = errors.Wrap(err, "readStringAndTrimDelimiter failed") - p.logger.Error(err) - return err - } - - if log == "" { - break - } - - // create event per log line - offset += len([]byte(log)) - event := createEvent(log, offset, info, objectHash, s3Ctx) - err = p.forwardEvent(event) - if err != nil { - err = errors.Wrap(err, "forwardEvent failed") - p.logger.Error(err) - return err - } - } - return nil -} - -func (p *s3Input) decodeJSON(decoder *json.Decoder, objectHash string, s3Info s3Info, s3Ctx *s3Context) error { - offset := 0 - for { - var jsonFields interface{} - err := decoder.Decode(&jsonFields) - if jsonFields == nil { - return nil - } - - if err == io.EOF { - offset, err = p.jsonFieldsType(jsonFields, offset, objectHash, s3Info, s3Ctx) - if err != nil { - return err - } - } else if err != nil { - // decode json failed, skip this log file - err = errors.Wrapf(err, "decode json failed for '%s' from S3 bucket '%s', skipping this file", s3Info.key, s3Info.name) - p.logger.Warn(err) - return nil - } - - offsetNew, err := p.jsonFieldsType(jsonFields, offset, objectHash, s3Info, s3Ctx) - if err != nil { - return err - } - offset = offsetNew - } -} - -func (p *s3Input) jsonFieldsType(jsonFields interface{}, offset int, objectHash string, s3Info s3Info, s3Ctx *s3Context) (int, error) { - switch f := jsonFields.(type) { - case map[string][]interface{}: - if s3Info.expandEventListFromField != "" { - textValues, ok := f[s3Info.expandEventListFromField] - if !ok { - err := errors.Errorf("key '%s' not found", s3Info.expandEventListFromField) - p.logger.Error(err) - return offset, err - } - for _, v := range textValues { - offset, err := p.convertJSONToEvent(v, offset, objectHash, s3Info, s3Ctx) - if err != nil { - err = errors.Wrapf(err, "convertJSONToEvent failed for '%s' from S3 bucket '%s'", s3Info.key, s3Info.name) - p.logger.Error(err) - return offset, err - } - } - return offset, nil - } - case map[string]interface{}: - if s3Info.expandEventListFromField != "" { - textValues, ok := f[s3Info.expandEventListFromField] - if !ok { - err := errors.Errorf("key '%s' not found", s3Info.expandEventListFromField) - p.logger.Error(err) - return offset, err - } - - valuesConverted := textValues.([]interface{}) - for _, textValue := range valuesConverted { - offsetNew, err := p.convertJSONToEvent(textValue, offset, objectHash, s3Info, s3Ctx) - if err != nil { - err = errors.Wrapf(err, "convertJSONToEvent failed for '%s' from S3 bucket '%s'", s3Info.key, s3Info.name) - p.logger.Error(err) - return offset, err - } - offset = offsetNew - } - return offset, nil - } - - offset, err := p.convertJSONToEvent(f, offset, objectHash, s3Info, s3Ctx) - if err != nil { - err = errors.Wrapf(err, "convertJSONToEvent failed for '%s' from S3 bucket '%s'", s3Info.key, s3Info.name) - p.logger.Error(err) - return offset, err - } - return offset, nil - } - return offset, nil + defer collector.publisher.Close() + collector.run() + return ctx.Cancelation.Err() } -func (p *s3Input) convertJSONToEvent(jsonFields interface{}, offset int, objectHash string, s3Info s3Info, s3Ctx *s3Context) (int, error) { - vJSON, err := json.Marshal(jsonFields) - logOriginal := string(vJSON) - log := trimLogDelimiter(logOriginal) - offset += len([]byte(log)) - event := createEvent(log, offset, s3Info, objectHash, s3Ctx) +func (in *s3Input) createCollector(ctx v2.Context, pipeline beat.Pipeline) (*s3Collector, error) { + log := ctx.Logger.With("queue_url", in.config.QueueURL) - err = p.forwardEvent(event) + client, err := pipeline.ConnectWith(beat.ClientConfig{ + CloseRef: ctx.Cancelation, + ACKHandler: newACKHandler(), + }) if err != nil { - err = errors.Wrap(err, "forwardEvent failed") - p.logger.Error(err) - return offset, err - } - return offset, nil -} - -func (p *s3Input) forwardEvent(event beat.Event) error { - ok := p.outlet.OnEvent(event) - if !ok { - return errOutletClosed - } - return nil -} - -func (p *s3Input) deleteMessage(queueURL string, messagesReceiptHandle string, svcSQS sqsiface.ClientAPI) error { - deleteMessageInput := &sqs.DeleteMessageInput{ - QueueUrl: awssdk.String(queueURL), - ReceiptHandle: awssdk.String(messagesReceiptHandle), + return nil, err } - req := svcSQS.DeleteMessageRequest(deleteMessageInput) - - // The Context will interrupt the request if the timeout expires. - ctx, cancelFn := context.WithTimeout(p.context, p.config.APITimeout) - defer cancelFn() - - _, err := req.Send(ctx) + regionName, err := getRegionFromQueueURL(in.config.QueueURL) if err != nil { - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == awssdk.ErrCodeRequestCanceled { - return nil - } - return errors.Wrapf(err, "SQS DeleteMessageRequest failed in queue %s", queueURL) + err := fmt.Errorf("getRegionFromQueueURL failed: %w", err) + log.Error(err) + return nil, err + } else { + log = log.With("region", regionName) } - return nil -} -func trimLogDelimiter(log string) string { - return strings.TrimSuffix(log, "\n") -} - -func readStringAndTrimDelimiter(reader *bufio.Reader) (string, error) { - logOriginal, err := reader.ReadString('\n') + awsConfig, err := awscommon.GetAWSCredentials(in.config.AwsConfig) if err != nil { - return logOriginal, err - } - return trimLogDelimiter(logOriginal), nil -} - -func createEvent(log string, offset int, info s3Info, objectHash string, s3Ctx *s3Context) beat.Event { - s3Ctx.Inc() - - event := beat.Event{ - Timestamp: time.Now().UTC(), - Fields: common.MapStr{ - "message": log, - "log": common.MapStr{ - "offset": int64(offset), - "file.path": constructObjectURL(info), - }, - "aws": common.MapStr{ - "s3": common.MapStr{ - "bucket": common.MapStr{ - "name": info.name, - "arn": info.arn}, - "object.key": info.key, - }, - }, - "cloud": common.MapStr{ - "provider": "aws", - "region": info.region, - }, - }, - Private: s3Ctx, - } - event.SetID(objectHash + "-" + fmt.Sprintf("%012d", offset)) - - return event -} - -func constructObjectURL(info s3Info) string { - return "https://" + info.name + ".s3-" + info.region + ".amazonaws.com/" + info.key -} - -// s3ObjectHash returns a short sha256 hash of the bucket arn + object key name. -func s3ObjectHash(s3Info s3Info) string { - h := sha256.New() - h.Write([]byte(s3Info.arn + s3Info.key)) - prefix := hex.EncodeToString(h.Sum(nil)) - return prefix[:10] -} - -func (c *s3Context) setError(err error) { - // only care about the last error for now - // TODO: add "Typed" error to error for context - c.mux.Lock() - defer c.mux.Unlock() - c.err = err -} - -func (c *s3Context) done() { - c.mux.Lock() - defer c.mux.Unlock() - c.refs-- - if c.refs == 0 { - c.errC <- c.err - close(c.errC) - } -} - -func (c *s3Context) Inc() { - c.mux.Lock() - defer c.mux.Unlock() - c.refs++ -} - -// isStreamGzipped determines whether the given stream of bytes (encapsulated in a buffered reader) -// represents gzipped content or not. A buffered reader is used so the function can peek into the byte -// stream without consuming it. This makes it convenient for code executed after this function call -// to consume the stream if it wants. -func isStreamGzipped(r *bufio.Reader) (bool, error) { - // Why 512? See https://godoc.org/net/http#DetectContentType - buf, err := r.Peek(512) - if err != nil && err != io.EOF { - return false, err - } - - switch http.DetectContentType(buf) { - case "application/x-gzip", "application/zip": - return true, nil - default: - return false, nil - } + return nil, fmt.Errorf("getAWSCredentials failed: %w", err) + } + awsConfig.Region = regionName + + visibilityTimeout := int64(in.config.VisibilityTimeout.Seconds()) + log.Infof("visibility timeout is set to %v seconds", visibilityTimeout) + log.Infof("aws api timeout is set to %v", in.config.APITimeout) + + return &s3Collector{ + cancellation: ctxtool.FromCanceller(ctx.Cancelation), + logger: log, + config: &in.config, + publisher: client, + visibilityTimeout: visibilityTimeout, + sqs: sqs.New(awscommon.EnrichAWSConfigWithEndpoint(in.config.AwsConfig.Endpoint, "sqs", regionName, awsConfig)), + s3: s3.New(awscommon.EnrichAWSConfigWithEndpoint(in.config.AwsConfig.Endpoint, "s3", regionName, awsConfig)), + }, nil +} + +func newACKHandler() beat.ACKer { + return acker.ConnectionOnly( + acker.EventPrivateReporter(func(_ int, privates []interface{}) { + for _, private := range privates { + if s3Context, ok := private.(*s3Context); ok { + s3Context.done() + } + } + }), + ) } diff --git a/x-pack/filebeat/input/s3/s3_integration_test.go b/x-pack/filebeat/input/s3/s3_integration_test.go index 1d6a400a7f1..cadb5da035b 100644 --- a/x-pack/filebeat/input/s3/s3_integration_test.go +++ b/x-pack/filebeat/input/s3/s3_integration_test.go @@ -16,21 +16,20 @@ import ( "testing" "time" - "github.com/stretchr/testify/assert" - - "github.com/aws/aws-sdk-go-v2/aws" awssdk "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/s3" - "github.com/aws/aws-sdk-go-v2/service/s3/s3iface" "github.com/aws/aws-sdk-go-v2/service/sqs" "github.com/aws/aws-sdk-go-v2/service/sqs/sqsiface" + "github.com/stretchr/testify/assert" - "github.com/elastic/beats/v7/filebeat/channel" - "github.com/elastic/beats/v7/filebeat/input" + v2 "github.com/elastic/beats/v7/filebeat/input/v2" "github.com/elastic/beats/v7/libbeat/beat" "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/logp" + pubtest "github.com/elastic/beats/v7/libbeat/publisher/testing" "github.com/elastic/beats/v7/libbeat/tests/resources" awscommon "github.com/elastic/beats/v7/x-pack/libbeat/common/aws" + "github.com/elastic/go-concert/unison" ) const ( @@ -77,228 +76,99 @@ func getConfigForTest(t *testing.T) config { return config } -func uploadSampleLogFile(t *testing.T, bucketName string, svcS3 s3iface.ClientAPI) { - t.Helper() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - file, err := os.Open(filePath) - if err != nil { - t.Fatalf("Failed to open file %v", filePath) - } - defer file.Close() - - s3PutObjectInput := s3.PutObjectInput{ - Bucket: aws.String(bucketName), - Key: aws.String(filepath.Base(filePath)), - Body: file, - } - req := svcS3.PutObjectRequest(&s3PutObjectInput) - output, err := req.Send(ctx) - if err != nil { - t.Fatalf("failed to put object into s3 bucket: %v", output) - } -} - -func collectOldMessages(t *testing.T, queueURL string, visibilityTimeout int64, svcSQS sqsiface.ClientAPI) []string { - t.Helper() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - // receive messages from sqs - req := svcSQS.ReceiveMessageRequest( - &sqs.ReceiveMessageInput{ - QueueUrl: &queueURL, - MessageAttributeNames: []string{"All"}, - MaxNumberOfMessages: &maxNumberOfMessage, - VisibilityTimeout: &visibilityTimeout, - WaitTimeSeconds: &waitTimeSecond, - }) - - output, err := req.Send(ctx) - if err != nil { - t.Fatalf("failed to receive message from SQS: %v", output) - } - - var oldMessageHandles []string - for _, message := range output.Messages { - oldMessageHandles = append(oldMessageHandles, *message.ReceiptHandle) - } - - return oldMessageHandles -} - -func (input *s3Input) deleteAllMessages(t *testing.T, awsConfig awssdk.Config, queueURL string, visibilityTimeout int64, svcSQS sqsiface.ClientAPI) error { - var messageReceiptHandles []string - init := true - for init || len(messageReceiptHandles) > 0 { - init = false - messageReceiptHandles = collectOldMessages(t, queueURL, visibilityTimeout, svcSQS) - for _, receiptHandle := range messageReceiptHandles { - err := input.deleteMessage(queueURL, receiptHandle, svcSQS) - if err != nil { - return err - } - } - } - return nil -} - func defaultTestConfig() *common.Config { return common.MustNewConfigFrom(map[string]interface{}{ "queue_url": os.Getenv("QUEUE_URL"), }) } -func runTest(t *testing.T, cfg *common.Config, run func(t *testing.T, input *s3Input, out *stubOutleter)) { - // Simulate input.Context from Filebeat input runner. - inputCtx := newInputContext() - defer close(inputCtx.Done) - - // Stub outlet for receiving events generated by the input. - eventOutlet := newStubOutlet() - defer eventOutlet.Close() - - connector := channel.ConnectorFunc(func(_ *common.Config, _ beat.ClientConfig) (channel.Outleter, error) { - return eventOutlet, nil - }) +func newV2Context() (v2.Context, func()) { + ctx, cancel := context.WithCancel(context.Background()) + return v2.Context{ + Logger: logp.NewLogger("s3_test"), + ID: "test_id", + Cancelation: ctx, + }, cancel +} - in, err := NewInput(cfg, connector, inputCtx) +func setupInput(t *testing.T, cfg *common.Config) (*s3Collector, chan beat.Event) { + inp, err := Plugin().Manager.Create(cfg) if err != nil { t.Fatal(err) } - s3Input := in.(*s3Input) - defer s3Input.Stop() - run(t, s3Input, eventOutlet) -} + ctx, cancel := newV2Context() + t.Cleanup(cancel) -func newInputContext() input.Context { - return input.Context{ - Done: make(chan struct{}), + client := pubtest.NewChanClient(0) + pipeline := pubtest.ConstClient(client) + collector, err := inp.(*s3Input).createCollector(ctx, pipeline) + if err != nil { + t.Fatal(err) } + return collector, client.Channel } -type stubOutleter struct { - sync.Mutex - cond *sync.Cond - done bool - Events []beat.Event -} - -func newStubOutlet() *stubOutleter { - o := &stubOutleter{} - o.cond = sync.NewCond(o) - return o -} - -func (o *stubOutleter) waitForEvents(numEvents int) ([]beat.Event, bool) { - o.Lock() - defer o.Unlock() - - for len(o.Events) < numEvents && !o.done { - o.cond.Wait() +func setupCollector(t *testing.T, cfg *common.Config, mock bool) (*s3Collector, chan beat.Event) { + collector, receiver := setupInput(t, cfg) + if mock { + svcS3 := &MockS3Client{} + svcSQS := &MockSQSClient{} + collector.sqs = svcSQS + collector.s3 = svcS3 + return collector, receiver } - size := numEvents - if size >= len(o.Events) { - size = len(o.Events) + config := getConfigForTest(t) + awsConfig, err := awscommon.GetAWSCredentials(config.AwsConfig) + if err != nil { + t.Fatal("failed GetAWSCredentials with AWS Config: ", err) } - out := make([]beat.Event, size) - copy(out, o.Events) - return out, len(out) == numEvents + s3BucketRegion := os.Getenv("S3_BUCKET_REGION") + if s3BucketRegion == "" { + t.Log("S3_BUCKET_REGION is not set, default to us-west-1") + s3BucketRegion = "us-west-1" + } + awsConfig.Region = s3BucketRegion + awsConfig = awsConfig.Copy() + collector.sqs = sqs.New(awsConfig) + collector.s3 = s3.New(awsConfig) + return collector, receiver } -func (o *stubOutleter) Close() error { - o.Lock() - defer o.Unlock() - o.done = true - return nil -} - -func (o *stubOutleter) Done() <-chan struct{} { return nil } - -func (o *stubOutleter) OnEvent(event beat.Event) bool { - o.Lock() - defer o.Unlock() - o.Events = append(o.Events, event) - o.cond.Broadcast() - return !o.done +func runTest(t *testing.T, cfg *common.Config, mock bool, run func(t *testing.T, collector *s3Collector, receiver chan beat.Event)) { + collector, receiver := setupCollector(t, cfg, mock) + run(t, collector, receiver) } func TestS3Input(t *testing.T) { - inputConfig := defaultTestConfig() - config := getConfigForTest(t) - - runTest(t, inputConfig, func(t *testing.T, input *s3Input, out *stubOutleter) { - awsConfig, err := awscommon.GetAWSCredentials(config.AwsConfig) - if err != nil { - - } - s3BucketRegion := os.Getenv("S3_BUCKET_REGION") - if s3BucketRegion == "" { - t.Log("S3_BUCKET_REGION is not set, default to us-west-1") - s3BucketRegion = "us-west-1" - } - awsConfig.Region = s3BucketRegion - input.awsConfig = awsConfig.Copy() - svcSQS := sqs.New(awsConfig) - - // remove old messages from SQS - err = input.deleteAllMessages(t, awsConfig, config.QueueURL, int64(config.VisibilityTimeout.Seconds()), svcSQS) - if err != nil { - t.Fatalf("failed to delete message: %v", err.Error()) - } - + runTest(t, defaultTestConfig(), false, func(t *testing.T, collector *s3Collector, receiver chan beat.Event) { // upload a sample log file for testing s3BucketNameEnv := os.Getenv("S3_BUCKET_NAME") if s3BucketNameEnv == "" { t.Fatal("failed to get S3_BUCKET_NAME") } - svcS3 := s3.New(awsConfig) - uploadSampleLogFile(t, s3BucketNameEnv, svcS3) - time.Sleep(30 * time.Second) - wg := sync.WaitGroup{} wg.Add(1) go func() { defer wg.Done() - input.run(svcSQS, svcS3, 300) + collector.run() }() - events, ok := out.waitForEvents(2) - if !ok { - t.Fatalf("Expected 2 events, but got %d.", len(events)) - } - input.Stop() - - // check events - for i, event := range events { - bucketName, err := event.GetValue("aws.s3.bucket.name") - assert.NoError(t, err) - assert.Equal(t, s3BucketNameEnv, bucketName) - - objectKey, err := event.GetValue("aws.s3.object.key") - assert.NoError(t, err) - assert.Equal(t, fileName, objectKey) - - message, err := event.GetValue("message") - assert.NoError(t, err) - switch i { - case 0: - assert.Equal(t, "logline1\n", message) - case 1: - assert.Equal(t, "logline2\n", message) - } - } + event := <-receiver + bucketName, err := event.GetValue("aws.s3.bucket.name") + assert.NoError(t, err) + assert.Equal(t, s3BucketNameEnv, bucketName) - // delete messages from the queue - err = input.deleteAllMessages(t, awsConfig, config.QueueURL, int64(config.VisibilityTimeout.Seconds()), svcSQS) - if err != nil { - t.Fatalf("failed to delete message: %v", err.Error()) - } + objectKey, err := event.GetValue("aws.s3.object.key") + assert.NoError(t, err) + assert.Equal(t, fileName, objectKey) + + message, err := event.GetValue("message") + assert.NoError(t, err) + assert.Equal(t, "logline1\n", message) }) } @@ -354,41 +224,23 @@ func TestMockS3Input(t *testing.T) { "queue_url": "https://sqs.ap-southeast-1.amazonaws.com/123456/test", }) - runTest(t, cfg, func(t *testing.T, input *s3Input, out *stubOutleter) { - svcS3 := &MockS3Client{} - svcSQS := &MockSQSClient{} + runTest(t, cfg, true, func(t *testing.T, collector *s3Collector, receiver chan beat.Event) { + defer collector.cancellation.Done() + defer collector.publisher.Close() - wg := sync.WaitGroup{} - wg.Add(1) - go func() { - defer wg.Done() - input.run(svcSQS, svcS3, 300) - }() + output, err := collector.receiveMessage(collector.sqs, collector.visibilityTimeout) + assert.NoError(t, err) - events, ok := out.waitForEvents(2) - if !ok { - t.Fatalf("Expected 2 events, but got %d.", len(events)) - } - input.Wait() - - // check events - for i, event := range events { - bucketName, err := event.GetValue("aws.s3.bucket.name") - assert.NoError(t, err) - assert.Equal(t, "test-s3-ks-2", bucketName) - - objectKey, err := event.GetValue("aws.s3.object.key") - assert.NoError(t, err) - assert.Equal(t, "server-access-logging2019-06-21-16-16-54", objectKey) - - message, err := event.GetValue("message") - assert.NoError(t, err) - switch i { - case 0: - assert.Equal(t, s3LogString1, message) - case 1: - assert.Equal(t, s3LogString2, message) - } - } + var grp unison.MultiErrGroup + errC := make(chan error) + defer close(errC) + grp.Go(func() (err error) { + return collector.processMessage(collector.s3, output.Messages[0], errC) + }) + + event := <-receiver + bucketName, err := event.GetValue("aws.s3.bucket.name") + assert.NoError(t, err) + assert.Equal(t, "test-s3-ks-2", bucketName) }) } From 30a141519911c933372624c9aaf4b7fc9d0ce8d5 Mon Sep 17 00:00:00 2001 From: Adrian Serrano Date: Fri, 2 Oct 2020 23:12:59 +0200 Subject: [PATCH 05/93] Increase index pattern size check to 10MiB (#21487) --- libbeat/tests/system/beat/common_tests.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libbeat/tests/system/beat/common_tests.py b/libbeat/tests/system/beat/common_tests.py index c9cdbc52cc0..920ee35e72e 100644 --- a/libbeat/tests/system/beat/common_tests.py +++ b/libbeat/tests/system/beat/common_tests.py @@ -4,6 +4,12 @@ from beat.beat import INTEGRATION_TESTS +# Fail if the exported index pattern is larger than 10MiB +# This is to avoid problems with Kibana when the payload +# of the request to install the index pattern exceeds the +# default limit. +index_pattern_size_limit = 10 * 1024 * 1024 + class TestExportsMixin: @@ -56,7 +62,7 @@ def test_export_index_pattern(self): js = json.loads(output) assert "objects" in js size = len(output.encode('utf-8')) - assert size < 1024 * 1024, "Kibana index pattern must be less than 1MiB " \ + assert size < index_pattern_size_limit, "Kibana index pattern must be less than 10MiB " \ "to keep the Beat setup request size below " \ "Kibana's server.maxPayloadBytes." @@ -68,7 +74,7 @@ def test_export_index_pattern_migration(self): js = json.loads(output) assert "objects" in js size = len(output.encode('utf-8')) - assert size < 1024 * 1024, "Kibana index pattern must be less than 1MiB " \ + assert size < index_pattern_size_limit, "Kibana index pattern must be less than 10MiB " \ "to keep the Beat setup request size below " \ "Kibana's server.maxPayloadBytes." From e50c9b3929fdab157000e01adcd07a9d92b05b37 Mon Sep 17 00:00:00 2001 From: Pius Date: Fri, 2 Oct 2020 19:40:29 -0700 Subject: [PATCH 06/93] Clarify input type configuration options (#19284) Clarify that all input type configuration options must be specified within the external configuration file: https://github.com/elastic/beats/issues/19148 --- filebeat/docs/reload-configuration.asciidoc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/filebeat/docs/reload-configuration.asciidoc b/filebeat/docs/reload-configuration.asciidoc index 10a706d4020..14c4826b6d8 100644 --- a/filebeat/docs/reload-configuration.asciidoc +++ b/filebeat/docs/reload-configuration.asciidoc @@ -33,9 +33,11 @@ definitions. TIP: The first line of each external configuration file must be an input definition that starts with `- type`. Make sure you omit the line -+{beatname_lc}.config.inputs+ from this file. - -For example: ++{beatname_lc}.config.inputs+ from this file. All <> +must be specified within each external configuration file. Specifying these +configuration options at the global `filebeat.config.inputs` level is not supported. + +Example external configuration file: [source,yaml] ------------------------------------------------------------------------------ From e9cb70f8805fe84889f27391314281f2a39514cc Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Sun, 4 Oct 2020 09:43:06 -0400 Subject: [PATCH 07/93] Bump version to ECS 1.6 in modules without ECS updates (#21455) For the Filebeat modules that required no changes to move to ECS 1.6 this updates the ecs.version field from 1.5.0 to 1.6.0. And update the ecs.version for Auditbeat, Packetbeat, and Winlogbeat. Relates #19472 --- auditbeat/cmd/root.go | 2 +- filebeat/module/apache/access/config/access.yml | 2 +- filebeat/module/apache/error/config/error.yml | 2 +- filebeat/module/auditd/log/config/log.yml | 2 +- filebeat/module/elasticsearch/audit/config/audit.yml | 2 +- filebeat/module/elasticsearch/deprecation/config/log.yml | 2 +- filebeat/module/elasticsearch/gc/config/gc.yml | 2 +- filebeat/module/elasticsearch/server/config/log.yml | 2 +- filebeat/module/elasticsearch/slowlog/config/slowlog.yml | 2 +- filebeat/module/haproxy/log/config/file.yml | 2 +- filebeat/module/haproxy/log/config/syslog.yml | 2 +- filebeat/module/icinga/debug/config/debug.yml | 2 +- filebeat/module/icinga/main/config/main.yml | 2 +- filebeat/module/icinga/startup/config/startup.yml | 2 +- filebeat/module/iis/access/config/iis-access.yml | 2 +- filebeat/module/iis/error/config/iis-error.yml | 2 +- filebeat/module/kafka/log/config/log.yml | 2 +- filebeat/module/kibana/log/config/log.yml | 2 +- filebeat/module/logstash/log/config/log.yml | 2 +- filebeat/module/logstash/slowlog/config/slowlog.yml | 2 +- filebeat/module/mongodb/log/config/log.yml | 2 +- filebeat/module/mysql/error/config/error.yml | 2 +- filebeat/module/mysql/slowlog/config/slowlog.yml | 2 +- filebeat/module/nats/log/config/log.yml | 2 +- filebeat/module/nginx/access/config/nginx-access.yml | 2 +- filebeat/module/nginx/error/config/nginx-error.yml | 2 +- .../nginx/ingress_controller/config/ingress_controller.yml | 2 +- filebeat/module/postgresql/log/config/log.yml | 2 +- filebeat/module/redis/log/config/log.yml | 2 +- filebeat/module/traefik/access/config/traefik-access.yml | 2 +- packetbeat/cmd/root.go | 2 +- winlogbeat/cmd/root.go | 2 +- x-pack/filebeat/module/activemq/audit/config/audit.yml | 2 +- x-pack/filebeat/module/activemq/log/config/log.yml | 2 +- x-pack/filebeat/module/aws/cloudtrail/config/file.yml | 2 +- x-pack/filebeat/module/aws/cloudtrail/config/s3.yml | 2 +- x-pack/filebeat/module/aws/cloudwatch/config/file.yml | 2 +- x-pack/filebeat/module/aws/cloudwatch/config/s3.yml | 2 +- x-pack/filebeat/module/aws/ec2/config/file.yml | 2 +- x-pack/filebeat/module/aws/ec2/config/s3.yml | 2 +- x-pack/filebeat/module/aws/elb/config/file.yml | 2 +- x-pack/filebeat/module/aws/elb/config/s3.yml | 2 +- x-pack/filebeat/module/aws/s3access/config/file.yml | 2 +- x-pack/filebeat/module/aws/s3access/config/s3.yml | 2 +- x-pack/filebeat/module/aws/vpcflow/config/input.yml | 2 +- .../module/azure/activitylogs/config/azure-eventhub.yml | 2 +- x-pack/filebeat/module/azure/activitylogs/config/file.yml | 2 +- .../filebeat/module/azure/auditlogs/config/azure-eventhub.yml | 2 +- x-pack/filebeat/module/azure/auditlogs/config/file.yml | 2 +- .../filebeat/module/azure/signinlogs/config/azure-eventhub.yml | 2 +- x-pack/filebeat/module/azure/signinlogs/config/file.yml | 2 +- x-pack/filebeat/module/cef/log/config/input.yml | 2 +- x-pack/filebeat/module/checkpoint/firewall/config/firewall.yml | 2 +- x-pack/filebeat/module/coredns/log/config/coredns.yml | 2 +- x-pack/filebeat/module/crowdstrike/falcon/config/falcon.yml | 2 +- x-pack/filebeat/module/envoyproxy/log/config/envoyproxy.yml | 2 +- x-pack/filebeat/module/googlecloud/audit/config/input.yml | 2 +- x-pack/filebeat/module/googlecloud/firewall/config/input.yml | 2 +- x-pack/filebeat/module/googlecloud/vpcflow/config/input.yml | 2 +- x-pack/filebeat/module/gsuite/admin/config/config.yml | 2 +- x-pack/filebeat/module/gsuite/drive/config/config.yml | 2 +- x-pack/filebeat/module/gsuite/groups/config/config.yml | 2 +- x-pack/filebeat/module/gsuite/login/config/config.yml | 2 +- x-pack/filebeat/module/gsuite/saml/config/config.yml | 2 +- x-pack/filebeat/module/gsuite/user_accounts/config/config.yml | 2 +- x-pack/filebeat/module/ibmmq/errorlog/config/errorlog.yml | 2 +- x-pack/filebeat/module/iptables/log/config/input.yml | 2 +- x-pack/filebeat/module/misp/threat/config/input.yml | 2 +- x-pack/filebeat/module/mssql/log/config/config.yml | 2 +- x-pack/filebeat/module/netflow/log/config/netflow.yml | 2 +- x-pack/filebeat/module/o365/audit/config/input.yml | 2 +- x-pack/filebeat/module/okta/system/config/input.yml | 2 +- x-pack/filebeat/module/rabbitmq/log/config/log.yml | 2 +- x-pack/filebeat/module/sophos/xg/config/config.yml | 2 +- .../filebeat/module/zeek/capture_loss/config/capture_loss.yml | 2 +- x-pack/filebeat/module/zeek/connection/config/connection.yml | 2 +- x-pack/filebeat/module/zeek/dce_rpc/config/dce_rpc.yml | 2 +- x-pack/filebeat/module/zeek/dhcp/config/dhcp.yml | 2 +- x-pack/filebeat/module/zeek/dnp3/config/dnp3.yml | 2 +- x-pack/filebeat/module/zeek/dns/config/dns.yml | 2 +- x-pack/filebeat/module/zeek/dpd/config/dpd.yml | 2 +- x-pack/filebeat/module/zeek/files/config/files.yml | 2 +- x-pack/filebeat/module/zeek/ftp/config/ftp.yml | 2 +- x-pack/filebeat/module/zeek/http/config/http.yml | 2 +- x-pack/filebeat/module/zeek/intel/config/intel.yml | 2 +- x-pack/filebeat/module/zeek/irc/config/irc.yml | 2 +- x-pack/filebeat/module/zeek/modbus/config/modbus.yml | 2 +- x-pack/filebeat/module/zeek/mysql/config/mysql.yml | 2 +- x-pack/filebeat/module/zeek/notice/config/notice.yml | 2 +- x-pack/filebeat/module/zeek/ntlm/config/ntlm.yml | 2 +- x-pack/filebeat/module/zeek/ocsp/config/ocsp.yml | 2 +- x-pack/filebeat/module/zeek/pe/config/pe.yml | 2 +- x-pack/filebeat/module/zeek/radius/config/radius.yml | 2 +- x-pack/filebeat/module/zeek/rdp/config/rdp.yml | 2 +- x-pack/filebeat/module/zeek/rfb/config/rfb.yml | 2 +- x-pack/filebeat/module/zeek/sip/config/sip.yml | 2 +- x-pack/filebeat/module/zeek/smb_cmd/config/smb_cmd.yml | 2 +- x-pack/filebeat/module/zeek/smb_files/config/smb_files.yml | 2 +- x-pack/filebeat/module/zeek/smb_mapping/config/smb_mapping.yml | 2 +- x-pack/filebeat/module/zeek/smtp/config/smtp.yml | 2 +- x-pack/filebeat/module/zeek/snmp/config/snmp.yml | 2 +- x-pack/filebeat/module/zeek/socks/config/socks.yml | 2 +- x-pack/filebeat/module/zeek/ssh/config/ssh.yml | 2 +- x-pack/filebeat/module/zeek/stats/config/stats.yml | 2 +- x-pack/filebeat/module/zeek/syslog/config/syslog.yml | 2 +- x-pack/filebeat/module/zeek/traceroute/config/traceroute.yml | 2 +- x-pack/filebeat/module/zeek/tunnel/config/tunnel.yml | 2 +- x-pack/filebeat/module/zeek/weird/config/weird.yml | 2 +- x-pack/filebeat/module/zoom/webhook/config/webhook.yml | 2 +- 109 files changed, 109 insertions(+), 109 deletions(-) diff --git a/auditbeat/cmd/root.go b/auditbeat/cmd/root.go index 89d0bfd20ca..bf3167e7106 100644 --- a/auditbeat/cmd/root.go +++ b/auditbeat/cmd/root.go @@ -35,7 +35,7 @@ const ( Name = "auditbeat" // ecsVersion specifies the version of ECS that Auditbeat is implementing. - ecsVersion = "1.5.0" + ecsVersion = "1.6.0" ) // RootCmd for running auditbeat. diff --git a/filebeat/module/apache/access/config/access.yml b/filebeat/module/apache/access/config/access.yml index 183de629867..b39221031f3 100644 --- a/filebeat/module/apache/access/config/access.yml +++ b/filebeat/module/apache/access/config/access.yml @@ -8,4 +8,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/apache/error/config/error.yml b/filebeat/module/apache/error/config/error.yml index 7aa0cdb7cf8..b8aa75eef7c 100644 --- a/filebeat/module/apache/error/config/error.yml +++ b/filebeat/module/apache/error/config/error.yml @@ -10,4 +10,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/auditd/log/config/log.yml b/filebeat/module/auditd/log/config/log.yml index 183de629867..b39221031f3 100644 --- a/filebeat/module/auditd/log/config/log.yml +++ b/filebeat/module/auditd/log/config/log.yml @@ -8,4 +8,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/elasticsearch/audit/config/audit.yml b/filebeat/module/elasticsearch/audit/config/audit.yml index 7aa0cdb7cf8..b8aa75eef7c 100644 --- a/filebeat/module/elasticsearch/audit/config/audit.yml +++ b/filebeat/module/elasticsearch/audit/config/audit.yml @@ -10,4 +10,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/elasticsearch/deprecation/config/log.yml b/filebeat/module/elasticsearch/deprecation/config/log.yml index f6c37b9e426..aa6f7d220f0 100644 --- a/filebeat/module/elasticsearch/deprecation/config/log.yml +++ b/filebeat/module/elasticsearch/deprecation/config/log.yml @@ -15,4 +15,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/elasticsearch/gc/config/gc.yml b/filebeat/module/elasticsearch/gc/config/gc.yml index 5f62e00c54c..12b9a0f874d 100644 --- a/filebeat/module/elasticsearch/gc/config/gc.yml +++ b/filebeat/module/elasticsearch/gc/config/gc.yml @@ -13,4 +13,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/elasticsearch/server/config/log.yml b/filebeat/module/elasticsearch/server/config/log.yml index b0b9e3f55d0..45a7f84018d 100644 --- a/filebeat/module/elasticsearch/server/config/log.yml +++ b/filebeat/module/elasticsearch/server/config/log.yml @@ -15,4 +15,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/elasticsearch/slowlog/config/slowlog.yml b/filebeat/module/elasticsearch/slowlog/config/slowlog.yml index 2d98286de6d..4c861ea7dc7 100644 --- a/filebeat/module/elasticsearch/slowlog/config/slowlog.yml +++ b/filebeat/module/elasticsearch/slowlog/config/slowlog.yml @@ -16,4 +16,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/haproxy/log/config/file.yml b/filebeat/module/haproxy/log/config/file.yml index c3e104f56d4..ac3846087c3 100644 --- a/filebeat/module/haproxy/log/config/file.yml +++ b/filebeat/module/haproxy/log/config/file.yml @@ -9,4 +9,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/haproxy/log/config/syslog.yml b/filebeat/module/haproxy/log/config/syslog.yml index 6fa56f8fe3c..e919d732e74 100644 --- a/filebeat/module/haproxy/log/config/syslog.yml +++ b/filebeat/module/haproxy/log/config/syslog.yml @@ -6,4 +6,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/icinga/debug/config/debug.yml b/filebeat/module/icinga/debug/config/debug.yml index 33c47850878..8b5c2ae7422 100644 --- a/filebeat/module/icinga/debug/config/debug.yml +++ b/filebeat/module/icinga/debug/config/debug.yml @@ -12,4 +12,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/icinga/main/config/main.yml b/filebeat/module/icinga/main/config/main.yml index 33c47850878..8b5c2ae7422 100644 --- a/filebeat/module/icinga/main/config/main.yml +++ b/filebeat/module/icinga/main/config/main.yml @@ -12,4 +12,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/icinga/startup/config/startup.yml b/filebeat/module/icinga/startup/config/startup.yml index a69343bd796..acc8bd2b782 100644 --- a/filebeat/module/icinga/startup/config/startup.yml +++ b/filebeat/module/icinga/startup/config/startup.yml @@ -12,4 +12,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/iis/access/config/iis-access.yml b/filebeat/module/iis/access/config/iis-access.yml index b4797039397..2c845646a4e 100644 --- a/filebeat/module/iis/access/config/iis-access.yml +++ b/filebeat/module/iis/access/config/iis-access.yml @@ -9,4 +9,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/iis/error/config/iis-error.yml b/filebeat/module/iis/error/config/iis-error.yml index b4797039397..2c845646a4e 100644 --- a/filebeat/module/iis/error/config/iis-error.yml +++ b/filebeat/module/iis/error/config/iis-error.yml @@ -9,4 +9,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/kafka/log/config/log.yml b/filebeat/module/kafka/log/config/log.yml index a745c79562c..f3cfc687526 100644 --- a/filebeat/module/kafka/log/config/log.yml +++ b/filebeat/module/kafka/log/config/log.yml @@ -13,4 +13,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/kibana/log/config/log.yml b/filebeat/module/kibana/log/config/log.yml index 1ad204c62fe..93daa116791 100644 --- a/filebeat/module/kibana/log/config/log.yml +++ b/filebeat/module/kibana/log/config/log.yml @@ -11,4 +11,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/logstash/log/config/log.yml b/filebeat/module/logstash/log/config/log.yml index af0e4c33735..407078dc115 100644 --- a/filebeat/module/logstash/log/config/log.yml +++ b/filebeat/module/logstash/log/config/log.yml @@ -16,4 +16,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/logstash/slowlog/config/slowlog.yml b/filebeat/module/logstash/slowlog/config/slowlog.yml index 080a2c4310d..4c6abc278c6 100644 --- a/filebeat/module/logstash/slowlog/config/slowlog.yml +++ b/filebeat/module/logstash/slowlog/config/slowlog.yml @@ -11,4 +11,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/mongodb/log/config/log.yml b/filebeat/module/mongodb/log/config/log.yml index 183de629867..b39221031f3 100644 --- a/filebeat/module/mongodb/log/config/log.yml +++ b/filebeat/module/mongodb/log/config/log.yml @@ -8,4 +8,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/mysql/error/config/error.yml b/filebeat/module/mysql/error/config/error.yml index 818cbab297d..8ceba377810 100644 --- a/filebeat/module/mysql/error/config/error.yml +++ b/filebeat/module/mysql/error/config/error.yml @@ -16,4 +16,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/mysql/slowlog/config/slowlog.yml b/filebeat/module/mysql/slowlog/config/slowlog.yml index 7b1a3bc28fd..6893491d869 100644 --- a/filebeat/module/mysql/slowlog/config/slowlog.yml +++ b/filebeat/module/mysql/slowlog/config/slowlog.yml @@ -13,4 +13,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/nats/log/config/log.yml b/filebeat/module/nats/log/config/log.yml index 183de629867..b39221031f3 100644 --- a/filebeat/module/nats/log/config/log.yml +++ b/filebeat/module/nats/log/config/log.yml @@ -8,4 +8,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/nginx/access/config/nginx-access.yml b/filebeat/module/nginx/access/config/nginx-access.yml index 7aa0cdb7cf8..b8aa75eef7c 100644 --- a/filebeat/module/nginx/access/config/nginx-access.yml +++ b/filebeat/module/nginx/access/config/nginx-access.yml @@ -10,4 +10,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/nginx/error/config/nginx-error.yml b/filebeat/module/nginx/error/config/nginx-error.yml index 797c45f6c38..173a43c5a1e 100644 --- a/filebeat/module/nginx/error/config/nginx-error.yml +++ b/filebeat/module/nginx/error/config/nginx-error.yml @@ -14,4 +14,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/nginx/ingress_controller/config/ingress_controller.yml b/filebeat/module/nginx/ingress_controller/config/ingress_controller.yml index 7aa0cdb7cf8..b8aa75eef7c 100644 --- a/filebeat/module/nginx/ingress_controller/config/ingress_controller.yml +++ b/filebeat/module/nginx/ingress_controller/config/ingress_controller.yml @@ -10,4 +10,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/postgresql/log/config/log.yml b/filebeat/module/postgresql/log/config/log.yml index f69d3e4d387..eeac43780bf 100644 --- a/filebeat/module/postgresql/log/config/log.yml +++ b/filebeat/module/postgresql/log/config/log.yml @@ -12,4 +12,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/redis/log/config/log.yml b/filebeat/module/redis/log/config/log.yml index 9d6cae46b38..1c6b6d6c084 100644 --- a/filebeat/module/redis/log/config/log.yml +++ b/filebeat/module/redis/log/config/log.yml @@ -9,4 +9,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/filebeat/module/traefik/access/config/traefik-access.yml b/filebeat/module/traefik/access/config/traefik-access.yml index 183de629867..b39221031f3 100644 --- a/filebeat/module/traefik/access/config/traefik-access.yml +++ b/filebeat/module/traefik/access/config/traefik-access.yml @@ -8,4 +8,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/packetbeat/cmd/root.go b/packetbeat/cmd/root.go index 82ab41da374..0a22e98a1a0 100644 --- a/packetbeat/cmd/root.go +++ b/packetbeat/cmd/root.go @@ -37,7 +37,7 @@ const ( Name = "packetbeat" // ecsVersion specifies the version of ECS that Packetbeat is implementing. - ecsVersion = "1.5.0" + ecsVersion = "1.6.0" ) // withECSVersion is a modifier that adds ecs.version to events. diff --git a/winlogbeat/cmd/root.go b/winlogbeat/cmd/root.go index 2cd26a9fe8e..988ac6f9f6c 100644 --- a/winlogbeat/cmd/root.go +++ b/winlogbeat/cmd/root.go @@ -37,7 +37,7 @@ const ( Name = "winlogbeat" // ecsVersion specifies the version of ECS that Winlogbeat is implementing. - ecsVersion = "1.5.0" + ecsVersion = "1.6.0" ) // withECSVersion is a modifier that adds ecs.version to events. diff --git a/x-pack/filebeat/module/activemq/audit/config/audit.yml b/x-pack/filebeat/module/activemq/audit/config/audit.yml index 3e0ec415541..f40bf16116f 100644 --- a/x-pack/filebeat/module/activemq/audit/config/audit.yml +++ b/x-pack/filebeat/module/activemq/audit/config/audit.yml @@ -9,4 +9,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/activemq/log/config/log.yml b/x-pack/filebeat/module/activemq/log/config/log.yml index 17f6bd869f2..55449d359f1 100644 --- a/x-pack/filebeat/module/activemq/log/config/log.yml +++ b/x-pack/filebeat/module/activemq/log/config/log.yml @@ -13,4 +13,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/aws/cloudtrail/config/file.yml b/x-pack/filebeat/module/aws/cloudtrail/config/file.yml index 5a56f210c79..c4436629e21 100644 --- a/x-pack/filebeat/module/aws/cloudtrail/config/file.yml +++ b/x-pack/filebeat/module/aws/cloudtrail/config/file.yml @@ -11,4 +11,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/aws/cloudtrail/config/s3.yml b/x-pack/filebeat/module/aws/cloudtrail/config/s3.yml index 2094f77c712..7c455f64f26 100644 --- a/x-pack/filebeat/module/aws/cloudtrail/config/s3.yml +++ b/x-pack/filebeat/module/aws/cloudtrail/config/s3.yml @@ -58,4 +58,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/aws/cloudwatch/config/file.yml b/x-pack/filebeat/module/aws/cloudwatch/config/file.yml index 5a56f210c79..c4436629e21 100644 --- a/x-pack/filebeat/module/aws/cloudwatch/config/file.yml +++ b/x-pack/filebeat/module/aws/cloudwatch/config/file.yml @@ -11,4 +11,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/aws/cloudwatch/config/s3.yml b/x-pack/filebeat/module/aws/cloudwatch/config/s3.yml index 073eca58ab2..5d2d75dc5d8 100644 --- a/x-pack/filebeat/module/aws/cloudwatch/config/s3.yml +++ b/x-pack/filebeat/module/aws/cloudwatch/config/s3.yml @@ -44,4 +44,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/aws/ec2/config/file.yml b/x-pack/filebeat/module/aws/ec2/config/file.yml index 5a56f210c79..c4436629e21 100644 --- a/x-pack/filebeat/module/aws/ec2/config/file.yml +++ b/x-pack/filebeat/module/aws/ec2/config/file.yml @@ -11,4 +11,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/aws/ec2/config/s3.yml b/x-pack/filebeat/module/aws/ec2/config/s3.yml index 073eca58ab2..5d2d75dc5d8 100644 --- a/x-pack/filebeat/module/aws/ec2/config/s3.yml +++ b/x-pack/filebeat/module/aws/ec2/config/s3.yml @@ -44,4 +44,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/aws/elb/config/file.yml b/x-pack/filebeat/module/aws/elb/config/file.yml index 498a7906457..e9470671e07 100644 --- a/x-pack/filebeat/module/aws/elb/config/file.yml +++ b/x-pack/filebeat/module/aws/elb/config/file.yml @@ -11,4 +11,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/aws/elb/config/s3.yml b/x-pack/filebeat/module/aws/elb/config/s3.yml index 073eca58ab2..5d2d75dc5d8 100644 --- a/x-pack/filebeat/module/aws/elb/config/s3.yml +++ b/x-pack/filebeat/module/aws/elb/config/s3.yml @@ -44,4 +44,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/aws/s3access/config/file.yml b/x-pack/filebeat/module/aws/s3access/config/file.yml index 498a7906457..e9470671e07 100644 --- a/x-pack/filebeat/module/aws/s3access/config/file.yml +++ b/x-pack/filebeat/module/aws/s3access/config/file.yml @@ -11,4 +11,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/aws/s3access/config/s3.yml b/x-pack/filebeat/module/aws/s3access/config/s3.yml index 073eca58ab2..5d2d75dc5d8 100644 --- a/x-pack/filebeat/module/aws/s3access/config/s3.yml +++ b/x-pack/filebeat/module/aws/s3access/config/s3.yml @@ -44,4 +44,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/aws/vpcflow/config/input.yml b/x-pack/filebeat/module/aws/vpcflow/config/input.yml index c9e88b6a743..d0c18047ca4 100644 --- a/x-pack/filebeat/module/aws/vpcflow/config/input.yml +++ b/x-pack/filebeat/module/aws/vpcflow/config/input.yml @@ -173,4 +173,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/azure/activitylogs/config/azure-eventhub.yml b/x-pack/filebeat/module/azure/activitylogs/config/azure-eventhub.yml index a4567959194..77c7ea3f5d0 100644 --- a/x-pack/filebeat/module/azure/activitylogs/config/azure-eventhub.yml +++ b/x-pack/filebeat/module/azure/activitylogs/config/azure-eventhub.yml @@ -13,4 +13,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/azure/activitylogs/config/file.yml b/x-pack/filebeat/module/azure/activitylogs/config/file.yml index 498a7906457..e9470671e07 100644 --- a/x-pack/filebeat/module/azure/activitylogs/config/file.yml +++ b/x-pack/filebeat/module/azure/activitylogs/config/file.yml @@ -11,4 +11,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/azure/auditlogs/config/azure-eventhub.yml b/x-pack/filebeat/module/azure/auditlogs/config/azure-eventhub.yml index 3633cc4e5de..43a051f8dd3 100644 --- a/x-pack/filebeat/module/azure/auditlogs/config/azure-eventhub.yml +++ b/x-pack/filebeat/module/azure/auditlogs/config/azure-eventhub.yml @@ -12,4 +12,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/azure/auditlogs/config/file.yml b/x-pack/filebeat/module/azure/auditlogs/config/file.yml index 937446eb523..8e77b860e99 100644 --- a/x-pack/filebeat/module/azure/auditlogs/config/file.yml +++ b/x-pack/filebeat/module/azure/auditlogs/config/file.yml @@ -10,4 +10,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/azure/signinlogs/config/azure-eventhub.yml b/x-pack/filebeat/module/azure/signinlogs/config/azure-eventhub.yml index dd8e1473a68..b971be2783c 100644 --- a/x-pack/filebeat/module/azure/signinlogs/config/azure-eventhub.yml +++ b/x-pack/filebeat/module/azure/signinlogs/config/azure-eventhub.yml @@ -12,4 +12,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/azure/signinlogs/config/file.yml b/x-pack/filebeat/module/azure/signinlogs/config/file.yml index 937446eb523..8e77b860e99 100644 --- a/x-pack/filebeat/module/azure/signinlogs/config/file.yml +++ b/x-pack/filebeat/module/azure/signinlogs/config/file.yml @@ -10,4 +10,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/cef/log/config/input.yml b/x-pack/filebeat/module/cef/log/config/input.yml index 49a2b1829be..cc911a8611f 100644 --- a/x-pack/filebeat/module/cef/log/config/input.yml +++ b/x-pack/filebeat/module/cef/log/config/input.yml @@ -28,4 +28,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/checkpoint/firewall/config/firewall.yml b/x-pack/filebeat/module/checkpoint/firewall/config/firewall.yml index 12440f8fffe..f447d2aacdf 100644 --- a/x-pack/filebeat/module/checkpoint/firewall/config/firewall.yml +++ b/x-pack/filebeat/module/checkpoint/firewall/config/firewall.yml @@ -23,4 +23,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/coredns/log/config/coredns.yml b/x-pack/filebeat/module/coredns/log/config/coredns.yml index be7f27f551f..4f9992ae2d4 100644 --- a/x-pack/filebeat/module/coredns/log/config/coredns.yml +++ b/x-pack/filebeat/module/coredns/log/config/coredns.yml @@ -9,4 +9,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/crowdstrike/falcon/config/falcon.yml b/x-pack/filebeat/module/crowdstrike/falcon/config/falcon.yml index 689bd725530..2af0eeca092 100644 --- a/x-pack/filebeat/module/crowdstrike/falcon/config/falcon.yml +++ b/x-pack/filebeat/module/crowdstrike/falcon/config/falcon.yml @@ -23,4 +23,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/envoyproxy/log/config/envoyproxy.yml b/x-pack/filebeat/module/envoyproxy/log/config/envoyproxy.yml index be7f27f551f..4f9992ae2d4 100644 --- a/x-pack/filebeat/module/envoyproxy/log/config/envoyproxy.yml +++ b/x-pack/filebeat/module/envoyproxy/log/config/envoyproxy.yml @@ -9,4 +9,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/googlecloud/audit/config/input.yml b/x-pack/filebeat/module/googlecloud/audit/config/input.yml index 4c30e23b5e3..b5e392ee0b6 100644 --- a/x-pack/filebeat/module/googlecloud/audit/config/input.yml +++ b/x-pack/filebeat/module/googlecloud/audit/config/input.yml @@ -34,4 +34,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/googlecloud/firewall/config/input.yml b/x-pack/filebeat/module/googlecloud/firewall/config/input.yml index d6579aa9f47..39648636c59 100644 --- a/x-pack/filebeat/module/googlecloud/firewall/config/input.yml +++ b/x-pack/filebeat/module/googlecloud/firewall/config/input.yml @@ -35,4 +35,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/googlecloud/vpcflow/config/input.yml b/x-pack/filebeat/module/googlecloud/vpcflow/config/input.yml index cf89526bbe5..f1976195687 100644 --- a/x-pack/filebeat/module/googlecloud/vpcflow/config/input.yml +++ b/x-pack/filebeat/module/googlecloud/vpcflow/config/input.yml @@ -34,4 +34,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/gsuite/admin/config/config.yml b/x-pack/filebeat/module/gsuite/admin/config/config.yml index b5c62d3657f..9ec19a83615 100644 --- a/x-pack/filebeat/module/gsuite/admin/config/config.yml +++ b/x-pack/filebeat/module/gsuite/admin/config/config.yml @@ -39,7 +39,7 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 - script: lang: javascript id: gsuite-common diff --git a/x-pack/filebeat/module/gsuite/drive/config/config.yml b/x-pack/filebeat/module/gsuite/drive/config/config.yml index 5f1bd6ecbf3..92e464164b0 100644 --- a/x-pack/filebeat/module/gsuite/drive/config/config.yml +++ b/x-pack/filebeat/module/gsuite/drive/config/config.yml @@ -39,7 +39,7 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 - script: lang: javascript id: gsuite-common diff --git a/x-pack/filebeat/module/gsuite/groups/config/config.yml b/x-pack/filebeat/module/gsuite/groups/config/config.yml index 46a3ed338d9..ba6890c80e4 100644 --- a/x-pack/filebeat/module/gsuite/groups/config/config.yml +++ b/x-pack/filebeat/module/gsuite/groups/config/config.yml @@ -39,7 +39,7 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 - script: lang: javascript id: gsuite-common diff --git a/x-pack/filebeat/module/gsuite/login/config/config.yml b/x-pack/filebeat/module/gsuite/login/config/config.yml index b501012b3d2..95ccc04ed2c 100644 --- a/x-pack/filebeat/module/gsuite/login/config/config.yml +++ b/x-pack/filebeat/module/gsuite/login/config/config.yml @@ -39,7 +39,7 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 - script: lang: javascript id: gsuite-common diff --git a/x-pack/filebeat/module/gsuite/saml/config/config.yml b/x-pack/filebeat/module/gsuite/saml/config/config.yml index 1e703737e0d..8d1c7645fda 100644 --- a/x-pack/filebeat/module/gsuite/saml/config/config.yml +++ b/x-pack/filebeat/module/gsuite/saml/config/config.yml @@ -39,7 +39,7 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 - script: lang: javascript id: gsuite-common diff --git a/x-pack/filebeat/module/gsuite/user_accounts/config/config.yml b/x-pack/filebeat/module/gsuite/user_accounts/config/config.yml index 773ab620173..dc9e3d353e6 100644 --- a/x-pack/filebeat/module/gsuite/user_accounts/config/config.yml +++ b/x-pack/filebeat/module/gsuite/user_accounts/config/config.yml @@ -39,7 +39,7 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 - script: lang: javascript id: gsuite-common diff --git a/x-pack/filebeat/module/ibmmq/errorlog/config/errorlog.yml b/x-pack/filebeat/module/ibmmq/errorlog/config/errorlog.yml index 2130bb419d2..bca14ecccaa 100644 --- a/x-pack/filebeat/module/ibmmq/errorlog/config/errorlog.yml +++ b/x-pack/filebeat/module/ibmmq/errorlog/config/errorlog.yml @@ -12,4 +12,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/iptables/log/config/input.yml b/x-pack/filebeat/module/iptables/log/config/input.yml index 6183661122a..bcfde17c3c5 100644 --- a/x-pack/filebeat/module/iptables/log/config/input.yml +++ b/x-pack/filebeat/module/iptables/log/config/input.yml @@ -55,4 +55,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/misp/threat/config/input.yml b/x-pack/filebeat/module/misp/threat/config/input.yml index 3ff985b07f3..26010fc478a 100644 --- a/x-pack/filebeat/module/misp/threat/config/input.yml +++ b/x-pack/filebeat/module/misp/threat/config/input.yml @@ -37,4 +37,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/mssql/log/config/config.yml b/x-pack/filebeat/module/mssql/log/config/config.yml index 31990fb32c3..0ac3d9da2c2 100644 --- a/x-pack/filebeat/module/mssql/log/config/config.yml +++ b/x-pack/filebeat/module/mssql/log/config/config.yml @@ -14,4 +14,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/netflow/log/config/netflow.yml b/x-pack/filebeat/module/netflow/log/config/netflow.yml index b34160bd6b9..f56ffeed226 100644 --- a/x-pack/filebeat/module/netflow/log/config/netflow.yml +++ b/x-pack/filebeat/module/netflow/log/config/netflow.yml @@ -31,4 +31,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/o365/audit/config/input.yml b/x-pack/filebeat/module/o365/audit/config/input.yml index d41a5bb9aab..ec30daef127 100644 --- a/x-pack/filebeat/module/o365/audit/config/input.yml +++ b/x-pack/filebeat/module/o365/audit/config/input.yml @@ -62,4 +62,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/okta/system/config/input.yml b/x-pack/filebeat/module/okta/system/config/input.yml index a544ab8dc65..487dfdf165e 100644 --- a/x-pack/filebeat/module/okta/system/config/input.yml +++ b/x-pack/filebeat/module/okta/system/config/input.yml @@ -67,4 +67,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/rabbitmq/log/config/log.yml b/x-pack/filebeat/module/rabbitmq/log/config/log.yml index bc46f2458c8..1f32bbf71cf 100644 --- a/x-pack/filebeat/module/rabbitmq/log/config/log.yml +++ b/x-pack/filebeat/module/rabbitmq/log/config/log.yml @@ -18,4 +18,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/sophos/xg/config/config.yml b/x-pack/filebeat/module/sophos/xg/config/config.yml index 86c12e9ec08..8585ff4a19c 100644 --- a/x-pack/filebeat/module/sophos/xg/config/config.yml +++ b/x-pack/filebeat/module/sophos/xg/config/config.yml @@ -27,7 +27,7 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 - add_fields: target: '_conf' fields: diff --git a/x-pack/filebeat/module/zeek/capture_loss/config/capture_loss.yml b/x-pack/filebeat/module/zeek/capture_loss/config/capture_loss.yml index 6b6fcf216f2..5794bd0ca6f 100644 --- a/x-pack/filebeat/module/zeek/capture_loss/config/capture_loss.yml +++ b/x-pack/filebeat/module/zeek/capture_loss/config/capture_loss.yml @@ -22,4 +22,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/connection/config/connection.yml b/x-pack/filebeat/module/zeek/connection/config/connection.yml index 8a79295724f..f6caa143689 100644 --- a/x-pack/filebeat/module/zeek/connection/config/connection.yml +++ b/x-pack/filebeat/module/zeek/connection/config/connection.yml @@ -102,4 +102,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/dce_rpc/config/dce_rpc.yml b/x-pack/filebeat/module/zeek/dce_rpc/config/dce_rpc.yml index 45010e08973..7d7f0c87353 100644 --- a/x-pack/filebeat/module/zeek/dce_rpc/config/dce_rpc.yml +++ b/x-pack/filebeat/module/zeek/dce_rpc/config/dce_rpc.yml @@ -58,4 +58,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/dhcp/config/dhcp.yml b/x-pack/filebeat/module/zeek/dhcp/config/dhcp.yml index f1a2f0ced3a..b049d571c14 100644 --- a/x-pack/filebeat/module/zeek/dhcp/config/dhcp.yml +++ b/x-pack/filebeat/module/zeek/dhcp/config/dhcp.yml @@ -120,4 +120,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/dnp3/config/dnp3.yml b/x-pack/filebeat/module/zeek/dnp3/config/dnp3.yml index 7730d2b6d85..67033a42cf2 100644 --- a/x-pack/filebeat/module/zeek/dnp3/config/dnp3.yml +++ b/x-pack/filebeat/module/zeek/dnp3/config/dnp3.yml @@ -68,4 +68,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/dns/config/dns.yml b/x-pack/filebeat/module/zeek/dns/config/dns.yml index 86a2022d695..9e0f7e952e2 100644 --- a/x-pack/filebeat/module/zeek/dns/config/dns.yml +++ b/x-pack/filebeat/module/zeek/dns/config/dns.yml @@ -203,4 +203,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/dpd/config/dpd.yml b/x-pack/filebeat/module/zeek/dpd/config/dpd.yml index acc6defd4df..67565ef21ab 100644 --- a/x-pack/filebeat/module/zeek/dpd/config/dpd.yml +++ b/x-pack/filebeat/module/zeek/dpd/config/dpd.yml @@ -57,4 +57,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/files/config/files.yml b/x-pack/filebeat/module/zeek/files/config/files.yml index 65c067609c9..0e0ce22e2bf 100644 --- a/x-pack/filebeat/module/zeek/files/config/files.yml +++ b/x-pack/filebeat/module/zeek/files/config/files.yml @@ -42,4 +42,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/ftp/config/ftp.yml b/x-pack/filebeat/module/zeek/ftp/config/ftp.yml index 51a3c053576..671a1162c66 100644 --- a/x-pack/filebeat/module/zeek/ftp/config/ftp.yml +++ b/x-pack/filebeat/module/zeek/ftp/config/ftp.yml @@ -86,4 +86,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/http/config/http.yml b/x-pack/filebeat/module/zeek/http/config/http.yml index 4c7c812d0cc..8a43f07f0dd 100644 --- a/x-pack/filebeat/module/zeek/http/config/http.yml +++ b/x-pack/filebeat/module/zeek/http/config/http.yml @@ -93,4 +93,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/intel/config/intel.yml b/x-pack/filebeat/module/zeek/intel/config/intel.yml index 5b73833ea35..80d565745f1 100644 --- a/x-pack/filebeat/module/zeek/intel/config/intel.yml +++ b/x-pack/filebeat/module/zeek/intel/config/intel.yml @@ -67,4 +67,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/irc/config/irc.yml b/x-pack/filebeat/module/zeek/irc/config/irc.yml index 54aaa9d4f4b..7e09f9fe376 100644 --- a/x-pack/filebeat/module/zeek/irc/config/irc.yml +++ b/x-pack/filebeat/module/zeek/irc/config/irc.yml @@ -72,4 +72,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/modbus/config/modbus.yml b/x-pack/filebeat/module/zeek/modbus/config/modbus.yml index d656ad0ab6a..a65bb88c0d0 100644 --- a/x-pack/filebeat/module/zeek/modbus/config/modbus.yml +++ b/x-pack/filebeat/module/zeek/modbus/config/modbus.yml @@ -73,4 +73,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/mysql/config/mysql.yml b/x-pack/filebeat/module/zeek/mysql/config/mysql.yml index 4c6e70d9f1c..963c8469fe1 100644 --- a/x-pack/filebeat/module/zeek/mysql/config/mysql.yml +++ b/x-pack/filebeat/module/zeek/mysql/config/mysql.yml @@ -72,4 +72,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/notice/config/notice.yml b/x-pack/filebeat/module/zeek/notice/config/notice.yml index 649d3f3ba97..07ccbfb76b4 100644 --- a/x-pack/filebeat/module/zeek/notice/config/notice.yml +++ b/x-pack/filebeat/module/zeek/notice/config/notice.yml @@ -104,4 +104,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/ntlm/config/ntlm.yml b/x-pack/filebeat/module/zeek/ntlm/config/ntlm.yml index c67f66b54b9..135086e19f9 100644 --- a/x-pack/filebeat/module/zeek/ntlm/config/ntlm.yml +++ b/x-pack/filebeat/module/zeek/ntlm/config/ntlm.yml @@ -86,4 +86,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/ocsp/config/ocsp.yml b/x-pack/filebeat/module/zeek/ocsp/config/ocsp.yml index 874a0fde6d9..933f829b747 100644 --- a/x-pack/filebeat/module/zeek/ocsp/config/ocsp.yml +++ b/x-pack/filebeat/module/zeek/ocsp/config/ocsp.yml @@ -64,4 +64,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/pe/config/pe.yml b/x-pack/filebeat/module/zeek/pe/config/pe.yml index 3df430d7dc9..cfb8ea8c4b0 100644 --- a/x-pack/filebeat/module/zeek/pe/config/pe.yml +++ b/x-pack/filebeat/module/zeek/pe/config/pe.yml @@ -33,4 +33,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/radius/config/radius.yml b/x-pack/filebeat/module/zeek/radius/config/radius.yml index 66fccaa3f5c..471275d5ece 100644 --- a/x-pack/filebeat/module/zeek/radius/config/radius.yml +++ b/x-pack/filebeat/module/zeek/radius/config/radius.yml @@ -58,4 +58,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/rdp/config/rdp.yml b/x-pack/filebeat/module/zeek/rdp/config/rdp.yml index de71448fb1b..071cc59fab9 100644 --- a/x-pack/filebeat/module/zeek/rdp/config/rdp.yml +++ b/x-pack/filebeat/module/zeek/rdp/config/rdp.yml @@ -88,4 +88,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/rfb/config/rfb.yml b/x-pack/filebeat/module/zeek/rfb/config/rfb.yml index 3adb14c55bf..417e9ab4dcd 100644 --- a/x-pack/filebeat/module/zeek/rfb/config/rfb.yml +++ b/x-pack/filebeat/module/zeek/rfb/config/rfb.yml @@ -73,4 +73,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/sip/config/sip.yml b/x-pack/filebeat/module/zeek/sip/config/sip.yml index 7aa30034de2..97885a1e2dc 100644 --- a/x-pack/filebeat/module/zeek/sip/config/sip.yml +++ b/x-pack/filebeat/module/zeek/sip/config/sip.yml @@ -95,4 +95,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/smb_cmd/config/smb_cmd.yml b/x-pack/filebeat/module/zeek/smb_cmd/config/smb_cmd.yml index 763379a7d88..885669e4c9c 100644 --- a/x-pack/filebeat/module/zeek/smb_cmd/config/smb_cmd.yml +++ b/x-pack/filebeat/module/zeek/smb_cmd/config/smb_cmd.yml @@ -101,4 +101,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/smb_files/config/smb_files.yml b/x-pack/filebeat/module/zeek/smb_files/config/smb_files.yml index c5f7c2e53e7..a3f02a7558f 100644 --- a/x-pack/filebeat/module/zeek/smb_files/config/smb_files.yml +++ b/x-pack/filebeat/module/zeek/smb_files/config/smb_files.yml @@ -61,4 +61,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/smb_mapping/config/smb_mapping.yml b/x-pack/filebeat/module/zeek/smb_mapping/config/smb_mapping.yml index 624454ed171..59a68e2054b 100644 --- a/x-pack/filebeat/module/zeek/smb_mapping/config/smb_mapping.yml +++ b/x-pack/filebeat/module/zeek/smb_mapping/config/smb_mapping.yml @@ -57,4 +57,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/smtp/config/smtp.yml b/x-pack/filebeat/module/zeek/smtp/config/smtp.yml index 5b2f6595df2..0af383a7187 100644 --- a/x-pack/filebeat/module/zeek/smtp/config/smtp.yml +++ b/x-pack/filebeat/module/zeek/smtp/config/smtp.yml @@ -67,4 +67,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/snmp/config/snmp.yml b/x-pack/filebeat/module/zeek/snmp/config/snmp.yml index 0c7e05ce6db..1fa024742be 100644 --- a/x-pack/filebeat/module/zeek/snmp/config/snmp.yml +++ b/x-pack/filebeat/module/zeek/snmp/config/snmp.yml @@ -69,4 +69,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/socks/config/socks.yml b/x-pack/filebeat/module/zeek/socks/config/socks.yml index f834e5d1bcc..99613e4bfe3 100644 --- a/x-pack/filebeat/module/zeek/socks/config/socks.yml +++ b/x-pack/filebeat/module/zeek/socks/config/socks.yml @@ -67,4 +67,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/ssh/config/ssh.yml b/x-pack/filebeat/module/zeek/ssh/config/ssh.yml index c855d49dff2..45f160cd99d 100644 --- a/x-pack/filebeat/module/zeek/ssh/config/ssh.yml +++ b/x-pack/filebeat/module/zeek/ssh/config/ssh.yml @@ -76,4 +76,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/stats/config/stats.yml b/x-pack/filebeat/module/zeek/stats/config/stats.yml index cdf243f7a45..45c22b6b3d2 100644 --- a/x-pack/filebeat/module/zeek/stats/config/stats.yml +++ b/x-pack/filebeat/module/zeek/stats/config/stats.yml @@ -97,4 +97,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/syslog/config/syslog.yml b/x-pack/filebeat/module/zeek/syslog/config/syslog.yml index a89601cb717..0d1b8fc05e5 100644 --- a/x-pack/filebeat/module/zeek/syslog/config/syslog.yml +++ b/x-pack/filebeat/module/zeek/syslog/config/syslog.yml @@ -57,4 +57,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/traceroute/config/traceroute.yml b/x-pack/filebeat/module/zeek/traceroute/config/traceroute.yml index 13a2a37cc69..741a0d7e454 100644 --- a/x-pack/filebeat/module/zeek/traceroute/config/traceroute.yml +++ b/x-pack/filebeat/module/zeek/traceroute/config/traceroute.yml @@ -45,4 +45,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/tunnel/config/tunnel.yml b/x-pack/filebeat/module/zeek/tunnel/config/tunnel.yml index ac636e9e7c0..243f83de760 100644 --- a/x-pack/filebeat/module/zeek/tunnel/config/tunnel.yml +++ b/x-pack/filebeat/module/zeek/tunnel/config/tunnel.yml @@ -56,4 +56,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zeek/weird/config/weird.yml b/x-pack/filebeat/module/zeek/weird/config/weird.yml index 5807f95927b..4a4bab4dc33 100644 --- a/x-pack/filebeat/module/zeek/weird/config/weird.yml +++ b/x-pack/filebeat/module/zeek/weird/config/weird.yml @@ -56,4 +56,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/zoom/webhook/config/webhook.yml b/x-pack/filebeat/module/zoom/webhook/config/webhook.yml index 207da5447e1..ac04136779c 100644 --- a/x-pack/filebeat/module/zoom/webhook/config/webhook.yml +++ b/x-pack/filebeat/module/zoom/webhook/config/webhook.yml @@ -33,4 +33,4 @@ processors: - add_fields: target: '' fields: - ecs.version: 1.5.0 + ecs.version: 1.6.0 From 92b34600fcb54985bb4b9ee35aa96c07086645ea Mon Sep 17 00:00:00 2001 From: Chris Mark Date: Mon, 5 Oct 2020 10:33:06 +0300 Subject: [PATCH 08/93] Fix leftover delpoyment example (#21474) Signed-off-by: chrismark --- metricbeat/docs/running-on-kubernetes.asciidoc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/metricbeat/docs/running-on-kubernetes.asciidoc b/metricbeat/docs/running-on-kubernetes.asciidoc index 786977cb294..f852c153e5e 100644 --- a/metricbeat/docs/running-on-kubernetes.asciidoc +++ b/metricbeat/docs/running-on-kubernetes.asciidoc @@ -192,11 +192,6 @@ $ kubectl --namespace=kube-system get ds/metricbeat NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE-SELECTOR AGE metricbeat 32 32 0 32 0 1m - -$ kubectl --namespace=kube-system get deploy/metricbeat - -NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE -metricbeat 1 1 1 1 1m ------------------------------------------------ Metrics should start flowing to Elasticsearch. From b9569ff7e7c4d6901e19bcdaf08bde084908de01 Mon Sep 17 00:00:00 2001 From: Michal Pristas Date: Mon, 5 Oct 2020 09:38:22 +0200 Subject: [PATCH 09/93] Prompt only when agent is already enrolled (#21473) Prompt only when agent is already enrolled (#21473) --- x-pack/elastic-agent/pkg/agent/cmd/enroll.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/elastic-agent/pkg/agent/cmd/enroll.go b/x-pack/elastic-agent/pkg/agent/cmd/enroll.go index 6a604554136..a92766b9c07 100644 --- a/x-pack/elastic-agent/pkg/agent/cmd/enroll.go +++ b/x-pack/elastic-agent/pkg/agent/cmd/enroll.go @@ -119,7 +119,9 @@ func enroll(streams *cli.IOStreams, cmd *cobra.Command, flags *globalFlags, args if fromInstall { force = true } - if !force { + + // prompt only when it is not forced and is already enrolled + if !force && (cfg.Fleet != nil && cfg.Fleet.Enabled == true) { confirm, err := c.Confirm("This will replace your current settings. Do you want to continue?", true) if err != nil { return errors.New(err, "problem reading prompt response") From 3d8e7dd6fe114905610500ffdd0d8921ced759df Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Mon, 5 Oct 2020 10:51:51 +0200 Subject: [PATCH 10/93] Include original error when metricbeat fails to connect with Kafka (#21484) --- metricbeat/module/kafka/broker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metricbeat/module/kafka/broker.go b/metricbeat/module/kafka/broker.go index b9e78c7dacc..2e558d7944a 100644 --- a/metricbeat/module/kafka/broker.go +++ b/metricbeat/module/kafka/broker.go @@ -119,7 +119,7 @@ func (b *Broker) Connect() error { c, err := getClusterWideClient(b.Addr(), b.cfg) if err != nil { closeBroker(b.broker) - return fmt.Errorf("Could not get cluster client for advertised broker with address %v", b.Addr()) + return fmt.Errorf("getting cluster client for advertised broker with address %v: %w", b.Addr(), err) } b.client = c From 67a2d38203dbf04fc1ae8a186253a1e61917eb28 Mon Sep 17 00:00:00 2001 From: Mariana Dima Date: Mon, 5 Oct 2020 10:52:49 +0200 Subject: [PATCH 11/93] Add support for app_state metricset (#20639) * mofidy doc * add metricset * chnagelog * config * work on app_state * remove extra * fix changelog * mage fmt update * work on mapping fields * work on tests * fix vis name * no default * fix tests * add headers --- CHANGELOG.next.asciidoc | 1 + metricbeat/docs/fields.asciidoc | 223 ++- .../metricbeat-azure-app-state-overview.png | Bin 0 -> 453884 bytes metricbeat/docs/modules/azure.asciidoc | 20 + .../docs/modules/azure/app_state.asciidoc | 24 + metricbeat/docs/modules_list.asciidoc | 3 +- x-pack/metricbeat/metricbeat.reference.yml | 8 + .../module/azure/_meta/config.reference.yml | 8 + .../metricbeat/module/azure/_meta/config.yml | 8 + .../module/azure/_meta/docs.asciidoc | 8 + .../metricbeat/module/azure/_meta/fields.yml | 4 + .../Metricbeat-azure-app-state-overview.json | 1509 +++++++++++++++++ .../module/azure/app_insights/_meta/data.json | 21 +- .../azure/app_insights/_meta/fields.yml | 4 - .../module/azure/app_insights/app_insights.go | 3 +- .../app_insights_integration_test.go | 10 + .../module/azure/app_insights/client.go | 3 - .../module/azure/app_insights/data.go | 229 ++- .../module/azure/app_insights/data_test.go | 20 +- .../module/azure/app_state/_meta/data.json | 32 + .../azure/app_state/_meta/docs.asciidoc | 12 + .../module/azure/app_state/_meta/fields.yml | 86 + .../app_state/app_state_integration_test.go | 38 + .../module/azure/app_state/app_state_test.go | 17 + .../module/azure/app_state/manifest.yml | 29 + x-pack/metricbeat/module/azure/data.go | 6 +- x-pack/metricbeat/module/azure/data_test.go | 4 +- x-pack/metricbeat/module/azure/fields.go | 2 +- x-pack/metricbeat/module/azure/module.yml | 1 + .../module/azure/test/integration.go | 20 + .../metricbeat/modules.d/azure.yml.disabled | 8 + 31 files changed, 2275 insertions(+), 86 deletions(-) create mode 100644 metricbeat/docs/images/metricbeat-azure-app-state-overview.png create mode 100644 metricbeat/docs/modules/azure/app_state.asciidoc create mode 100644 x-pack/metricbeat/module/azure/_meta/kibana/7/dashboard/Metricbeat-azure-app-state-overview.json create mode 100644 x-pack/metricbeat/module/azure/app_state/_meta/data.json create mode 100644 x-pack/metricbeat/module/azure/app_state/_meta/docs.asciidoc create mode 100644 x-pack/metricbeat/module/azure/app_state/_meta/fields.yml create mode 100644 x-pack/metricbeat/module/azure/app_state/app_state_integration_test.go create mode 100644 x-pack/metricbeat/module/azure/app_state/app_state_test.go create mode 100644 x-pack/metricbeat/module/azure/app_state/manifest.yml diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index fb1fae91eb0..68b77a46037 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -349,6 +349,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Add missing info about the rest of the azure metricsets in the documentation. {pull}19601[19601] - Fix k8s scheduler compatibility issue. {pull}19699[19699] - Fix SQL module mapping NULL values as string {pull}18955[18955] {issue}18898[18898 +- Add support for azure light metricset app_stats. {pull}20639[20639] - Fix ec2 disk and network metrics to use Sum statistic method. {pull}20680[20680] - Fill cloud.account.name with accountID if account alias doesn't exist. {pull}20736[20736] - The Kibana collector applies backoff when errored at getting usage stats {pull}20772[20772] diff --git a/metricbeat/docs/fields.asciidoc b/metricbeat/docs/fields.asciidoc index ae34419db2e..26e83c19050 100644 --- a/metricbeat/docs/fields.asciidoc +++ b/metricbeat/docs/fields.asciidoc @@ -4665,6 +4665,16 @@ type: keyword The subscription ID +type: keyword + +-- + +*`azure.application_id`*:: ++ +-- +The application ID + + type: keyword -- @@ -4696,17 +4706,44 @@ application insights -*`azure.app_insights.application_id`*:: +*`azure.app_insights.start_date`*:: + -- -The application ID +The start date -type: keyword +type: date -- -*`azure.app_insights.start_date`*:: +*`azure.app_insights.end_date`*:: ++ +-- +The end date + + +type: date + +-- + +*`azure.app_insights.metrics.*.*`*:: ++ +-- +The metrics + + +type: object + +-- + +[float] +=== app_state + +application state + + + +*`azure.app_state.start_date`*:: + -- The start date @@ -4716,7 +4753,7 @@ type: date -- -*`azure.app_insights.end_date`*:: +*`azure.app_state.end_date`*:: + -- The end date @@ -4726,13 +4763,183 @@ type: date -- -*`azure.app_insights.metrics.*.*`*:: +*`azure.app_state.requests_count.sum`*:: + -- -The metrics +Request count -type: object +type: float + +-- + +*`azure.app_state.requests_failed.sum`*:: ++ +-- +Request failed count + + +type: float + +-- + +*`azure.app_state.users_count.unique`*:: ++ +-- +User count + + +type: float + +-- + +*`azure.app_state.sessions_count.unique`*:: ++ +-- +Session count + + +type: float + +-- + +*`azure.app_state.users_authenticated.unique`*:: ++ +-- +Authenticated users count + + +type: float + +-- + +*`azure.app_state.browser_timings_network_duration.avg`*:: ++ +-- +Browser timings network duration + + +type: float + +-- + +*`azure.app_state.browser_timings_send_duration.avg`*:: ++ +-- +Browser timings send duration + + +type: float + +-- + +*`azure.app_state.browser_timings_receive_uration.avg`*:: ++ +-- +Browser timings receive duration + + +type: float + +-- + +*`azure.app_state.browser_timings_processing_duration.avg`*:: ++ +-- +Browser timings processing duration + + +type: float + +-- + +*`azure.app_state.browser_timings_total_duration.avg`*:: ++ +-- +Browser timings total duration + + +type: float + +-- + +*`azure.app_state.exceptions_count.sum`*:: ++ +-- +Exception count + + +type: float + +-- + +*`azure.app_state.exceptions_browser.sum`*:: ++ +-- +Exception count at browser level + + +type: float + +-- + +*`azure.app_state.exceptions_server.sum`*:: ++ +-- +Exception count at server level + + +type: float + +-- + +*`azure.app_state.performance_counters_memory_available_bytes.avg`*:: ++ +-- +Performance counters memory available bytes + + +type: float + +-- + +*`azure.app_state.performance_counters_process_private_bytes.avg`*:: ++ +-- +Performance counters process private bytes + + +type: float + +-- + +*`azure.app_state.performance_counters_process_cpu_percentage_total.avg`*:: ++ +-- +Performance counters process cpu percentage total + + +type: float + +-- + +*`azure.app_state.performance_counters_process_cpu_percentage.avg`*:: ++ +-- +Performance counters process cpu percentage + + +type: float + +-- + +*`azure.app_state.performance_counters_processiobytes_per_second.avg`*:: ++ +-- +Performance counters process IO bytes per second + + +type: float -- diff --git a/metricbeat/docs/images/metricbeat-azure-app-state-overview.png b/metricbeat/docs/images/metricbeat-azure-app-state-overview.png new file mode 100644 index 0000000000000000000000000000000000000000..cffc6e4ef038e0e7132b0bc87b68b27df29b80b0 GIT binary patch literal 453884 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%0AlJ7SDs1Ac;N`uk%eRdkh3)QVhrgGqy56Eiy#} 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

NZ$rkfL$ZX6D|3%7mUYj2%63%n!xT zTWtXH9Dx>;!rElRVvqx#QBRD=(q0gAS3U=huWKp?sK{)SIvmg}8H6c^5TuO}57 z@Q`zz*560tqCJl`TCezX_#|BagY$E#f086k6Q3}53kV39ix*(mX@Y-_hum z_v^aATrgsCy2QKxaJBUol;9wS;Vv$)54s;4?lVE9&HhQGYD^ZN3<_IinbF*lKgy|h z#X>_PRaa=3+|q`dIEA!{#jRVn+Km`Pu3CoZtdZR+wJ;L7rx+;j{#;mBgK9@#-!ZxJ zIc4Lg9aLtcTiU5%UESdIJCgh>094wNIo;e1V+}7t+(L5OdI^rVd>(U65UBKj)E-CFqI zH<8y^h%(p|8aZy2k?hpsgfBRx zl@=dFg0@5LVHB^YWs79J&~NCe8QyX^-HrGw=`VKa8-8|deCH$5x4`#U913z1WI?dt zb{=n35j@4hm(EPdzfQAJ30!<}+J-D6(0M5uWBLnOwqc`mv8Q%GMB-y*j|eLcZxn55 zYlRjS{gpe%<(JAzg8*~>t zow`-RU97Q|nAXnTo2+x77XPOB1~Bh~*-h42+qFX#t+M!zlJ2i$iUr5z&t=tOt>`Kg znG8!xd#5Ws8)-VX!}ub}eE2hlBAxLHKisQrIoHmOL?9~tazg44N*L(;e*H1__r=GV zd`W?~ymwH?{>b9HKsuVW-2g6J9!xvj-F;SKPbxZG;fQW`hmRkCizW8F;_HpaoG-ob z?L}wPCF1W3B+%~Ornn7@5?&p?nJ@2G?&r*_pQ+G`%DXjSYCqM14M1gF=Y>0(QdXYR z7FKE)hV}X6nSFxA`W5|$QcBE%#TUrq&G%DFQ+2O;U`tq*gbpIPkL+5oHaSM4ITyE3 z%^7&A%e8AY4LRA>#L)o7h191JhOuaSa9|pXuIlu-8|{(;huNPmdNRy_ZqTLOg)`Zt zUw;P74g{6%_J4zWAFr0AvW(v6#n{_UW%PP78@79Gtt=?8qB!@6-JL+s@7YNXbLc^&JQE2BE>M~?F`zDmYZ#i z^Y)RXi}99m^q?`-H2`I_34~}ehoqN*@ptvFWK5=<_~xhs1G*9=7PC9O_Qo`0-CPWu z&JkDKw_Ic0-x=hyK!0i9WJ*z_3{KUIy^`#jXdVN8#HFd^<;(e^`TV|{**V#o)ZhTt zaJLPt?aNqBjgVXm!;M4bl{xue7>d0YDXnNzjF{Ugg)vKZCaC~Y zSg>62?y{ImeOI6`e^JK6!}zS9Yj7ESY`-27Jr)fP=XjKx-te+W3?4@Cg6hUt-YK*r znOa^9M7Qh#b64=CVxg|zRl3IVMSZFx8RS$K?DaaZ#vBeTf|L|1$F?oFVB*4qP$2XUQ^ zR=$ZwuWX25-f*z;9Tew6K6T;pR5TnFOwr4O))UbIz;6wR9n9&xomnai-ID{9ece28 zk#Q%h0#U2ud#aeLV+=!LoJ|3COz)Eu#Z{^5+jOds*HhtOW|(t-9yQjB-eb4YiJe+A zLI>t};P`w=InIqQ)Ql})!35Hk!+ujUJO51y`-c-8MNb`p&#l$z8<|^*!F$!8TgOyX z{3IYKco6Z-plD{cW*8&Q_KZPi5+FO<=FU^aG(s#lyR{PSRcf~25Xj$tuGn)|%# zCHn!0^dgkSQj_dUKK4%Wc@9)rVLI`++r_)Z0Ciq2%}+mGY1EXgCh;Ngo@d}S?FYmI zNzKR;vs<`+5u|Fle&O>$VZpT7pX-RWXQP;x9gi8=3$IbrSn-nQ90eBHE=mngYsVIh z4gG+Ub#G_ zU?-f2SYzreuV^9a-oAa-dD%XJWEVr)og8dAN3C6>*9H-S3$~mm^5t-NQOg4gRkRm_@$ciGdKn=<+gP&-cs_UcnA)a~64o z!3^sbudhP8EcAF+Qf6t(WAbU5w~AVZK^O!KuE(wC)akX>o2{K5z>Lw=sh+QGn#UMM zm5$FXf>TX_LoZuid>Erir8Gam#L}4)irwWBRb}C=v>d7xv9y(F*+8K_7UN9c$sbJi zH=H8VosXlStnlh)`L(I*%d!q1Vkgf=BJG+xDlssHW zAa04@5nR3vhQmf*2UQIhm8f6jz3M=;oM0x3EA{+XsbaN-rcf21EMAfRSim=T&b0#? z!Q)yVTh}13V&SFNB?pg6U34w+$))mK29=)GWasH95D6VzV;P)QQmWk(9Ap}_IFG2m zR~gX2^E)^bqoDS9I%|DVxj*;hr3QrB!dW)rsC)e?Uh-U5oVq<=blg=?kfxS3HLugI zE;H6|UsS_m?Mhk&t_lZERyI0~DRuiyp4zV4`;cx~x;Uv!qnoo+u>9T0stU9iLrvw9 zh(F$Qd*y)n?+m|`_Hu=ClS&25r}Md{l$5Du^gaxrYk~%>NiN=NXJ-PPpF9W1;7vFK z@KFp^_#tHz8ivVdkV4Jg@Pz<2`NlQ0;y@ib7}W`~8YSunv}TS|76M|HXNoW+=2grv z=dT?pZhfE4=OSXyN14lKe{W40x7R`-A}&N!gQ*^4RNh|lSnfR-d~)S84S%T{%yxVr zfM7=s=iYQIZ?&_dcF+i#-ONK2dPi$MAJKQR$6knrqR`*~3m2ln)P=HNAC~?H>i*1m zDVpl@N|{sg^4vi@ni~bZudL?^kdA4H67qfPzJ@Ga?VQ2fEX?4f5gUNJ3*SE2pD1lg zYt;uWJr?1C*7=ffsr@{41NoNYXL@B|hpdJs;ub%zYpfuh8vEBQ}yqrBJ22J1_X=`}a-8ioE)<}E&ysi5Z1@l)6MC0JNaJJV}2Ulj)`@5EJ{nk_Q}zhedB&-%PV|! z|0CjP`p#xQb7VE_zypLKei!EEQv%8SA_Dd`6d=sWY6a=1@bg5BcihpcZ7qW=Lt-$3Jbnf(B8?19_l*^tz|x553?MGL#Q<8vc52^QEBOwaJ$>X?havx z@#<3ivewb>jB|r1xA6Q=QpVenR)+)3H z<|?mNqS?!c^+Wvj-<4hE${XPR+j6Ss!XJ`Lri3#mD$b{|>!CY#9b;$}mI(p^)}2a& zKPpr~X=oIpu#k0QghDe#Pjt)7_l}oF-*m%M48P|O@Gcx@b*E~v+Lss zHd6Do4``X$Dnqtts=JjU5X7$B_q_k>#lL}K)47Y;M~o^Ynq!$z-;qbnu$xx`%wS-__{p&Ob0e<~< zLvQszMM=MIsyDv&wFoG78`yBKdq$WL<{l~cJ0ZBh2c9K8#HGJBnp+%(j(_r=1%bFb zGI3k9k=U!MUiZhaN;wN!)E}RH`iv^A)qHh!Fn8DvF<(D@`m`=gQ2G5jd-y>Yl>hre z^hK8vb;iS;jXz8G$4T4B6-;pF|H3ZL&l#ohpm&S2_I*i!+$CWLt>VO4qJY3?HvqAk zih7@7VT3h4mMLhUgYY*T>kqdXpAn2482DA7(obPk4s@UAav%M#{4m*@sXde3zx9?-|8f97nFqRaJ@@Zb|Y=cXT1&h8aL+%yQ*Zx%L~oRC;VG$TIx2&d~K3zmmJ$s72LwL;D05+V{&gRCh;@&;xhI=N)j>u=r0-pqcy-=!^NMHV)ZBNz)o9Edet7Kn-cpoolD$t?H z{S}Ut0&*au0*K+E#dsZkg5kvK!yz`LbMlO+@`rUZux73eY%o$j{Hbu{tPkw~pv8Ku zN@+u&t&9C4R^5;4=FdZsZHvMnaeFKo(hJtSg>aX+q+fQSuXjlNC!x*!yA-{$BKBGp z{s-V&lOgpD;SR7ek^7O$qUPO4SznLMt~iRBn@Y2y@9-RviCt}UFv^>wG?f0n_TeWT zp7>!JWuw~8`KHkI=0R!CR#dr^4S;%i6n%V;AU`cmTZw=GrO^0+fE%^lA- zyUk1{&g~opQy+QeKgePKABsmdH%!%O8r1gZ*MJ>MCk!Om4#(+XP)UhaVh(gO6+dQj zj(KU^29zoKD7?v4rSMpF?7;3~+b@`L&;-s#^h%)illgi!eixgg8#fW>FcPslVbJwr z6dRY|AsEJ$342yq{TiF`$xc8}ATF=vRLLoy3!kdp9;{aZF2kK}&OHBjviIZZk;Wu; zzSo++TkHzl?MW#>!t%Wki6&Gb%wo9g#_&ZR1zd&rYpJj;(neK4A<=q+=yE+Abk&O= zt2Exea}MY2gxN;1hhEcvumsD19$j!VACf5(b5b*Ueb%aMyG@DvV41bk4CZddPxBn# z)${J(f$hB)*R6@AqlrU9@lZPhkFfxWpu1OfOIOrYqr*&Mh?Og${!)rs{nhkRDv zCDa2nimMa{3z2vDC&{`Kn0$3V?jDQ;E6idL*q;jCgE+Wx-#(m-O6=Np&)F|3M* zfFkOkjP-woNR^|i(f96Mhz3NZ)CINivE-%UXg2r9?wjoLz%`7?vXfZltMeLbXk4t> z5)J?qo#H>p+pA&sS*0}htE45cTtN6fqrq7D8hC0YS`Wgoll=GxkHJIy2zndS~xm4>g8t;y{&rHu3%C$};Q0R6FLr#6Ty zeSz!7orU1ss>P`zDRwIqinLvcYw*`=4}kVinWn2N-I&krjN;r|)a@+%GX?6QvmCpA zpcGAlFHSME!6^cMWk{NAjCl)pu6T3>YZJ??OL7ao74Bf`Js4yd_~BP}@o@;0u62P_ zsP)SiTMW$)90Z7+qWkT87-5k5u-D0aK@}(%lbrC>mpNsQpQtd^54kp8@M0MUlAj?s z!4k+cRtoJwL!NI&6#wV6_N66#Jb@4sp|M~78Ir_2g8=y6%=#AC87X~4-p~^3<>y*-U zuPtA6pcX4#aAff6x?IXFw079D9=eN5dt0M@sk1IeCsqfr-HE^vu!3vZXn}j99BH=Ep&0aSmwwj?S#FDRh?3+*w-fm|KqTH*7lNKXBva z>h;;sIdJHiwwVtLh|3=C-@gxg{#I)pX|8W)nvM&+98@P0=e`4BVXDkXR_s=Xd^Ly0 zp;LUk@z}tpPd|>Z!r@U$62)(mi}?(5zp>bGp&zNRFfo5Fy0T0>&NK+@_fyp?bJ=c* zQ-uvyMwZ76<Kq+~Z3;>*ypdb7BavMni6#-+Tow*# z6pR^;&=&&HixI(C`IX!Xe#^2B6mzxFN}{ffGLJCNZ@0FGObDr=sQSK3NM-lO=)a zDK`zpo3)temd}(iW``D`REvF#6cs zF`^{do!|=ecxoVNILgi>_fN#LvBuVz&bT@~?R}+mn)}t4<2(G9+_Z>o=n8 zNHIZoYf)#1KNHa z-5j&u&oGyv2u+|dG2Y+dnT0VCsZVRU)TwReK<*xD~K1bB2n{XiL zf-#;~Swn=ZR;ZmQ)MaF#ONs^K9YRA~I)<8r4HrRF6LPW@Xsp43>PC`EFl*4(L(ur2 zLe;-Wo4*J%bqBTTXnMPHo%OR!`21W#o$mjVA8BkM5ZjZ2&B(DjB0Fscq{}U-hgJMm zp921^UQ^;uF87Bc1@NNny)|P>1`ayyiM*!uc)5xNGIkinmQ#aBMS5x~#Y>h}0St&8 zrdKl{*NpA7 zgji;DSfEKKvnK1vA}?RgE%W#5vMCOYmo*UMUf#lFBG{evMIc-P!s*5U(nVokZ;Rqr zZ}h;~a#miGm>MTRurFUw$MrrknJr@Wp-nz3K$@v9+4-VdqkL?;m$mAJIYGCelf!|n z0&XO(+?fczAXL$f-llMj8wztRw5c$Arqm{4ypBv+WCl7o;8a)@6+s1ii~k{V{~^=- z#s4M{LK*X%RJdqH(2alcUaH&k5a?x&pIvsuiRPM172Af&sWis=(ED58>yI13EFrxv zDe|8+Z#_{01Z!%|_XL;gu=sIu$!CEp{ekOkwHCIdSJ?%$iH0o4Yu7&W+*T-+FGEmV^-Xs7A>{TY+IMFejX7I_zo+d8K(9pSU?QXkWZTmm;MA<0t zotq>TqqI8{O?HX;rB6IOzsp1c zJ}G4D-`KE8H9a_Z@wE)|L)!4ttnmQp2BF#>5g@_t&6Z)F69L*$D0Q!o6gMY$0bGKF zD5bt`SY)Dm7nr~YC!Ws%#Jp&J)@qi;dO_N{rP2O$?2Q6?pE-@blzQ7SkB0}7z_`>e z+yP`~*44r55n2uNX#vcVS_V$~^p4wrqR`lUhcDx0zXIeoP1R`;wEOb5f)n~_@_L+V z)jv?|AE~?fgkX2I7HeFPdQx6==``qAUQjKpCW>4JY7@z*Ct=t@-8qmBSCPH(`q_~M z4-M+#DA{beC5!(zuE<-3$=M2TMu2)WUhng;s?+4ySdZOAm~9-2o;__CnxZ6> zr^#9nUPoWP7hQN`3chlWTlH*?P_b!rN~3jgTG+H<|ISic2Jtx&*L}-dsk`Pl>EOM4 z_u7PJ^k^?Es$VqURsRRJN__rw`6jXSM)vX?FJ5~w!2(s!7}0&5(N}cm^3TU?_v6)N z;>0xYP`S9dT#%-Yp9-*q2@E-TQ@*|)nd*#hWv*X;>ZYsYGw_p?! zo>>^1HQWyPgmd&_9pk$9A|fKuQP{I8drrER0Cwm!;oq0(juzFaUCKB&f=D_RhYVV2 z6=0u;0DYbDdl?YSG~gpab!^ZIt9rEy9w&9zrNr~iE6b*2Rk;rzLLNkY`oY9=sB)fk zUY%O9$^1-S+zUG1$YwHTc6RoAWfypkN9q62Sx)w2{`A9bCstB(Gr0fs6RRNl6 zYBwlhVZERhc3>kHJsHgGckRFrbDK{`W$_B_qO^BoYCwDa9?-uU6v5z9xFm(DN_o_c zj9#RWq)p(JIorHs!oCZ?yITV=IRgx zSax;1{axO(xd|tx%)~QwOwo92)xfJv0=;VOzzC=uzWM2YUsFl1yeU16-HiHog=#9E zld*^Onq%6Z28>Ts*gYXTDNkW`yO9p72SI%%XSIET@YoRLO{M1BRiLlb3BOQo<1(fn zEmyrwQg*8YLjU~0Vl5y7X)MaSyg}F=8C{+A8tjgg1TIHXS1xM8awYmVuzgoVcyU3J zx)4lOZf8c0;NGBwh$b0Xorn9zd7{_rOD~<}N2-xYM zfFRw5R6$CBfDi=%=~6;(>exU)q=^VfhXfKS0SP3rQbLm&=_);;LkJ{5zVi!AdDeU1 z&&>PB``71MGi$k`cyiixoxS(9uk#j|q{u&~EQK~X8OwCRic%9W2C-qJrm_Rz)DEpd zi;fbzXB1;41?q9Kg07;L%X`r!S?-MqgImsePJL1w-?pj@%0S-i`4I?LwlD?i0r?|y3Ki1eyRDaT|oISt3UQ-RYRO|e@f>(f%u*ENlp#~; z)mtBrA1@w=Jr4IjSS_x*^~aCl3_RP z9ZJPR!F}gp_EQJ_2cuI9?vu3NO`mT%{7I6}W_0U`w~Cr#^I}7uwD=h|RFit*mw%Z& z0mw`>MN~l&eQj}~`B^&6nEmwzXrx-uQ>yvX*vKozx#!$iS(-#MY zqMi?Yp!)}3?&6!D(X?y{lF?kb-HTStS|t{|D0m!=?i@ihrMT~8L#3Eum7KDa|F)IS zB8bOA+1^Q2%WGz_%RhQLTJH?~zCYfyZa_U5@79HC98vsZjYh_Dw0w0RnP60Ihgq2%xyZAE8Guty5Le^g<`Ou>k z;wH#3#&qHA+!D=)m)Hn!_kEN9C&2PAU%7X^*BzbWQ9N5em&?G!T6J6^u7jPoS3Mqx zCMQJW!d<(5Tupu9YG(XaRaeT|WjC7y7~rtLfB70Yt&m;m)lh(s2VbH1*bU2Xpg&RZ zoged^T8^gqCKKdXGH%@Ht)kU0tY1E`v75La=eD$*8K=<_!OiwCu~!v1+jx#lECLz8vbD@6^H;Xit-5U^>PTF1nWBOY+(WfdcGiY#ezn7Ld!ov(q+n z@s<&Db~_fTxn>O1SoEd;0t%~}hne(Bgm*6}!Kf-;a2=l><(BKsk7+Sf^jvrg05)JW zrvkZYLAo+9xOJ}gKQU{6fhXr=0UkjvvrC2}#pUh7DVDaDozTp@@f!Lh3dDCi*rcso z5_$?(s)zJmB2brXM{CDqtoXzk#RX_~@SVZx$eH84Pf}Bzi~pzydj3G$;W1I@pKi&& zAc$`bF?d^mg8lwnOarKUKo0ftuzIj*b+w55CiAZo;doQ%sVrKC$Lp&BHR--p>qJo? z1GQ_m(?$zQ~xAPS?H9{~m8;$O1*jdm_ z0iZbKgg^myE^wb|?K4;_a}zPJqkho0H2RO7f%l-3`h|k82D_^MqRJbI+(lQR6b>AN z6oXJAZLh3cLeQkiyQDl<4di|?|2QaAB8GB-L}8dV8#I>`RFNgprn(z05AX-G3!Pt6 z$YP$Fi2GyCS(FRWa8f__|DXo@gf^Rl+u+=W>W2Z$Ux&T^)CiP71zO*lf{dj^EEA;w zdD?T27wUfm=X-aNLTUG~yr7_jkKAYB*IR|mcw+${R#*nFXL9w`9$+X(g8W-Hg**eX&Nv}H0NQjjc-4P@xILR?#KKd+8=0mK0JRGaW@HJSu zIal#O!)^|U;B?)r(+?|$w7RfCDbI^w$650<)@&{!d9Y(_u&@WbcBwI$HN$44kk2&q0$Mt&r6%C1PQ=@YR4W^xe?~ok6HZM|3fSlLKou)- zp132g5Z7FySgWMaQ-KVD)07L?L}Y)s`^#T5XR9~%{L*~ z;uetlReFs|y%>y;0~RvPEnZ)KzK5Hua~ROGuBm0zWK8EH1r}Jq1LyPp9Qs%KiUVH0 zttg~=VHATOdFI~_#E2bLqh$OqOf_?gmJkg%nAHZ5rW3m!WKIvP%P(&HlZDplN(=Xt zpG~uqY%W{~NOx6)I)}A@oAk{WoM{@d@D;xRWG#5wDZO~vQ`hf_mSqnNK9@)V~}x*JVdPOFxHeK$Vmbms)A%R@{P8rFs zR62z7pBZ&*fxF@)oExv9Yw~JPH3bv{hu0mHH)ArA7;4d&vr`P^Bkpkje5N#!KxA)IIUG;kbsgv1_it*zw=6_fqb|E+3@c<+FVT^}$D%a+~ce9(ussA?RdKVfZ|mRnyqeVifNgF7fFc&2CU zjActOjTQ=-Aq}-PC#^azE6Iwxlbau|O~7TB#H=tha5#ZMW%xgsN9lrA0~_uzZL}$B z%)>YtzzNUhcykDEdysk`H3bMlSZ$IV>EC&+#h?3s>a|vJvTD(E8w`PP@a?ccH$8UN zj1B}ox|N=Hvc@1YJO|&DV~vY~1$>%6rS=GyryS z`61AP{9fI9+_vn26{szKMg1FYJZ_pfU^%QM8NWs|VAnTH(N5@auYGx5Rgy4NAbtUm?j1S_mTl80%)9l&DNp#bQRAw(JbYXaCCniRty>_}o z54f(6Y?K=SKzuq1aIJI618TNq<>Kz3i4kdPN>JOn#Bgy!^)&oq=jxaTb$N^^@765^ zc)udid(?(fG{tQ_g=FePWLfmPI#)Ug()=B@VGj3GK$}A5YRg~?E&IX9RB9?`pQcUe z>XLy!vqE%y2nkd|iVie^X_xqD5#Y+5;!p~Rzx!PuzV{qBCI8yql~4>;=~*p^ePNc& zmr7YUX6ciVP+@=*CVd!zu-`76yYZOH$uC0Xh8+q{E18&@?mYL*SkjteMrw?T2- z3LxKxLHhAtB^2K3WE|cOS#(AeOOu#6qN^6rYJE0iKLfqh^dX!)N5xEo*DzBzUp5{Y z-=xJFvt>(itIh>_^+F{9quD*02?~--!ju66J(@F`*W5O!1Ilst+ZWy|-Dz(u|%cTmhe61H^qRJ(xv;4hIA||pGHSNqCAF-v%4>8e&Vz6Ei9L=l2cnJ zX5LNWyDLADuwNi^l;fH~{fBxkLh1Bg0TAn-9hk7UCJaPAefpv+j4{cmotb$fn(_UC_{8fXGmS`+7k zbxqRo4X-l3`1xIXh~d`thg}0$EP3p9(01Gg42$ZYvKH-&aRog8 z*CcW4^-f!9@IFLaV4 z5J=U#2sv*cPzn4B)x+K&^Pgl)p{^CzyGEFU+^p7{~!!bFJY zQ?3)bk18{AbZm_y;DC*|9xthEG-PxeuEO~qz;pi>=2ocI_vggGtZ)cw`Mvi7X9p>) zmpc8Y=lm13&dSanVgCoi&w))EBn9m1k_&_i5(9a$`gyzD)* zul|%=oE?y)8D3`3F{cZHw#PXR8u;zXKNN6D+w}!in+c%OELd&-T#%b-#F^z=kC7tt~{c*CcPV`x9Zc{BNE%J+auwGsj_8@0<4wm2hQW7vp z`C6iJ=U+FKxj;L!R~tHMywSS)G-ax3|A*%1os;7pzV6H~-iD0%CDWS%f3s0R9$pGsj7Z(?o>-z<@;bdi^aI@ALj9z-T-J0y?^& z$a6?en!y8gHP3}s&Bn4Fmq8n`x#evFT7z%^R{*?T^_Kw%@Pk^gOfOi<_((T&vmsFN zKKDFr_41{3J!rDgz66*i4diVapSeD@7!*aA_>GBuY7FM}9t;t)0IyY>pZB2MzWeVzSUn%=u z0ODLbZ|{bJwDDzAIKz94T@GoVptdGi04%Pq5#F8?fWfO1fMNap)SU+f%SfT}J;*M+ zYk5J>8>9}9n!Rv_8h<#txXAQ?!c(deEs%CjjrBtrY-w4F3AT`!kV>-NnoaYru-1)e za!XD&lT$<&V%i326B93I9csjAsptH=7eNX9;ecjC%DCEs1*&$wbsqA&`9pOaJBp&f zvE|l`G3QXIzxEibd_DHO_|JXGrcL4Hq|o#7F5PZ!L5M0p?M}>qjoV1M6+tX;F{d^y zK_b4a4E?=u--h4gj?ZIbc}v@?Aa*(&=V+9QaI`Gu+(r91Fw))F$g-0hq2BHHMiD8H zP8-w>7$o>`dW0A`>$}~BeB>LI`bHqY?zw5f+2#(LX(3+HZJkt}c(vIqd$PL_;shj_ z5QS40@zXe(eDb8Ul~V$w8P=!=n&D;ULI&RusDX#9o&E;^yI)RyZGnGY^F|>h0BRmV z3|@0uUa6mMT+IWXiN16OGz< z=630c!s)xI4hxFngeZ#;Ao(Q_DFKb8cLIShTXcpRK=8qn2&_|0wDxh?5xUyaOhnzP zqoXPINvatzGlS>Q791{98W&1rdsx}o{m@Gk6G1GSy2Qwy)*XT*%4A>$UOXg?O`?1m z{Ico}nz4+PVOIaZe1#jI2a=9uHD#f;=vvtqC5(IFCYB-Xp-gsb z;F<1{Sg(!c;H8h+tQCs6K9yQnMc_hSK# zU`MXtB_t@JJr?}O&Cr(%y?yLdYigab^`+-9z|f16RR1q=>4Jk1Sz08fCUuEj7pk(9 zx3VxG#G7JRUQ8B-uGHm_E>vCe@NA9(Bap=${`y3=wY}F#rxJ3h>-K@og^S%H30tCQ zQCf)G$$p$5$aN!Kq**K$kjVKKiBfi3IF#23HSpPu^awjJ7g!~)eBL9Hoi6zR3feoB zmaqaN18J~>TgRw*$qOgAu|s7CHUU=0ulHMrP5dQG3+#)GoCM$Fj&;Vmah-%igh$>W z=8B3)oRO_nn{Br#;f`$jcF5{lhfN;uW!$@bS|-Ci#TK-Xxb@z+*(1jmxC}zr&+M&D z9T}`vquai|%>|~#4R2TVuWHi8>dHhmN`YGiWV9z#lSrlT?Cz%@)=naNb@aa}mu8B9g; zA2=W*OQX)x6cM`!!U1Upji#L)tH3s3Z#lgifTm5qyYSRcZ}~r|{HC=`sM?e9=zp}@ z8>CnC4=0#vTU6*necOXp>L{S-;{>cf4A^tgJeO)`XG~|aNpW9Vx=wa2XU|Q}(Lqu# zN4oW;yHg4>J?_0H1HI@cy3&r0cWI>4fDM8DMt_7id%Tf!E%@ApSz-Sy8w3!Vg#*^) zi3SM8=3~F;%Xj~+Z%9&1Ngj14cACfrh7e8%>26qUvWz=B-8H6YCY~ug z=3r^b0*mM1mojd?U`}^lAfnK;2I9g2`v4+n$kI>&5(}}EWtc7|Ad1a!;7*>8?zJ8t zOu|HHL|+})k#eDP@86x49QLn)kU0d}QB+K>_Lul(JVl?hlB79xEz#U#PeoJ=ux;Z3 zpBlPgIURHh105|OD3%=+qaAXd^||HUzTcJZun?n}CO_KS(&FFa51%dqzU65&P`oqe zibb~sCNQ>hqy$>dmbsbhJyf}TIfvod*99-aSTX_h%l3OpUq}R=ZNCi94fA?=A>3ck zT*{cbyu$ja3F5BIL~PAR%bIAxnu__z=P|}7Di^b_CLpC-nNy2T)6(kI9E;DH0DY4bb6KRTb9Qg-df zx5Ztg2LRhcNdook8~aS$*zy>?orYqS*oVVFlp1t)blfGKbfd9axesY*aUP0dRP=qVuB15RL>0`hSy&n1yb7PuoGhdw+Z+akGTQ7f|vNOXn}s z4rLc2n2*bWLr-0kc!~r~{%kq9(`surC?&n_db-W>0BpTXoyg=!s<&tnuyd=$ zw~SNUkyOAy&H-aRPoOt-?eYS)NxTxc7t23%vB?MR;zK%WK>Cq$dwHWZi0EyqbM9fS z*nySBgTV3%8?u_UvYIK~C=>}=yF7$D%fr#{mVqr=N-s$e%^MAj<*a62jhn~0ujqTlh5%Vi-K$tNb>^zFKlLd*hoTR! zA%qN;)p-_K^Q$WKVUa5VYWRjxZeYu~tOuR8fWsDJxIoeZFkHCqJYp5xj5}OFk!2Fv zv7m!innTBc`Vn{H>i3~$9K9stq#c16(8!S%WG*Pt=$$mo0-CpkjmMu1M`4Q}bQR2qL+}S5&O^3`jl)rW}`vOH`p#Oup(gKhL77#d5}Q zH?DuClnsgs{j5_Jy9vmnQLB zT_QLKumC!MZU@hGi3q^e{&$m|{p~n0gnoRlPS~mIBc6$Pzn}+k5>~im<6+sOn?7#uF@8meA z32k|Ba>P7yM{0>cgqkL!um*!g*JM1fKm+$lV6h~jYaz+6;HuCw#svKJ*Zr5E9fqF* zcU`Kdl!aeMaOyb^vP$YWery@4Ozq_j7K)>FRcaBLXsq)^>FJ zUb+SMb#c*K>7YYOz1*f(i8&*@+dAcH`{Q;x5@cr0Ds9)h8H8Q-SzPenR!}cz*LP1< zu`F8*z%wTjpv%1|_j4_H*co3;iNdrc=@xduMlbCs#7m>U9@1uDX&n6E)MZ{>x_>}b zOwC^SxAtLtzZ<(HN12y!s$~?kZ%X;>b8Dz`;&Yp-`g7~b_wV1&WafKU2%?GodO4L+ zvo?f&J@tM1jf*9+Z7e&h0cBP9TnfYO4a>G3B;5blmY1{HhumCUWAEo5oNEe9tNk%* zE%W)h!B`OwiDZGV#6C##@V%UXeDvtiFi??#68=-6>DW;V?8=?fmQjN0u7Wj()+KUJ z3ZzPmeuB&)kf#?0f{=foh8?m0!Fxw|>aNup+4t0jkMB~}NmoZ#8mN$4@4XDQbovyr z1~VdT>kO*UxYvO@6(C!Xnb{^mvJ4&O;o?YN!_oAn;VVV99V#vEs{z^k)O4S_OJHJuqw7>ozN0hMD)PybqLCXm&vQ;)%`h~~^!v0gb*6Ut z$Pcm9n0f8Tzi7*y+MbbdWzT*MbrRtr{6K){;@nIwVT{Sg%N48A9 zl~Rdij~aL`v&2WvZhEHtb_TTjx?FEUGSs%%VqegU8F07AchMr5QuplhuCd z`g01y(QUYB-(#|?@d}YVQJX%g0~^fShbhLwHm5`B3kQY=)I4e^;mkZ|!epfTY}VS7yGI>s#o6TzPG% zDr4*93w@|Cd!_a&JaI69s~b~Z&k_pC5ZIw{97|YBxQ<^S_|w)*EGe&pUG@RG@4StN zQDpDq zX`mZklk3jFa0U-`i$5}(_q-#aP+7P%k3SsGodowMich%p5G`4Lr95BhZLS&0O`EEF;4VeRENu3ix3>|{%>ri#SkPGaa<3Jd`OiEx!2qGP z<&NxyTMx=Eb0$)jGm!{r!rR(%4S@0Wdl8$lQx%F3QxXys#fuMscJJbEzTyp2)K`|aB!0I>!JD(#?C-xxT?O*MZ#RiyX#_0Q3a7OZbfE+0xB z;dQL+mtrVYK5cc&05c5@P%v-sA%)}~UqbU7o}jM5km6`KR2N&jw0^%G@2g9wG3kWy z|IDRM9rr(C+3FMNfA1JH#Nn(0%RM5p(!03U($%i1g%aUwO15P_DV-`KuNBbE>M~fP|pjwc~-QzWp zj1=}t=iyu1jf1WKab;FZ=LZMY+X~8iQb>SY+pds2# zseHOOo^y?<=x68tcnC;4pFHT*4{RTH}tn z329;7?lNytwd=>&4EAbbfkB_x|J>bM3)RcCmr*XZu)f%elihRs!F??w$XUb{tzaqs zwPnck$>VCFp9jtK(JN8O#Ad!zfy8^iLM!#>p`5udY>@g3`}SmB8-bui zQLb>N2J*lXM_!CwJ*e0{+oWYETxo1~9XVGSlUD@4>Pi3?vt_rdM!1qS<*RppuhE2& zojEMLL-pZI)$B-R^=vH}_0mH-_>%2RaO#Hx&wlk5T8uJ-m*Uu5dSi&l@`H%1 zKcX$VS1*-c7rIl9gD}{+_$lf7)^~`x9lITC?5!d8j06t%mS+dt`l-(x+XlWw`A*DX z*=uRVCtVW_$)37a!gg0tK9w=WVs`h>D4tGOo%sk`?wdw*_Ooa1H0(KF3f@;&2H#pc zm?eT(7t5{2%q_w*pAW${+x3-BRt`rhjMhaampACvY_zvsnd(WXH>wcRB%6(rpG;(S z|KKbS6!N&ZR=~7|{SFoDO#Bf=O`$`tS{YkxUK4PbvU#uWx7st4<~34I@jbD*I*~m7 zn!53B09(fN5v_7oJn*GuWl5HguR6KpRX@hFgi>95e3L0Zak=LY27ut!@};LoT;owx zExM#%MvXs-wYYU|_&GK!Z|U(0Jzl}iV@_v$O8b?^DX zY(doki3ncomnfvSp*WRyKW}t?9>3?bb?(Y!;c|CRQGHUY=X`fjei`OMAPH$W2;+Lp zE4-e!upUigS40pJ7N0(PT+99=fr3-otyWd!OjSS+{H|xlg!xwNN<1keS<7C zjp#pFWISvM8*1%KkSZb`DhyD;vkG-V)by9+nn_UNJhKXmQY}!h9p517Dw5nYoD#YFkNZKl>_?UqLy;LNJGOwZ6l) z4;a~1ZZ^WnXzII?6HBsT-L0H`k^1Mc1$IT}FCK7(`%d);ttTdojlAlQ+_=b1&dv%( zOj#{wln%5(kqb)Cj$|#{M656sq;J{df-*NR-Zv%@SOno$PiJa{59a>v;^Pl;4b4F(5 zL8ji-ZfCcO6M?eZv(|e3GvD^K&7TkdNqFe|;)TJ1h1H5y?=_qD?Uhgn(y{<&%pGTpI>?@q@{(lAR-g{3$vUnHb~ zTB&t}m*WT@tA647#9I#LsHza{?!EpI8T_U5E5_)<)M`ci$`nJoo?HF;S?u^OdU~wb zH>#UK;+8ZinQ-eOF{JIw#gIpjJWhn&26qO?@Kn#9`3@T0%Q#H2s_PCEBrSJeho|8? zL>jInzdYQsge-CCE;H+N?LL{?WO68W3FXwJZ9jF?(bYK-%)hpyf8<1e8efoFmF3xf zj-?wE{SNy6F<30B0ETy1|e$qsgt4Amu&g zO6jpeL_Y_KMzOMJ1hSyUL-mNq+k>pU!JE{#FDUIrvVD`|d+Y72=-=%?$~d{YySa4S z{XneXrXz>iJyfCNJT^WaLtx3+wMq+f2Trrp*^9c%Gt+6>W^v&~-vw2IL?{+LpiuXd z2o2Q{;x)Hbm%)^wk#c2JibNzM z4hq1c1nO5FebIRvhuoT9vxg>(NYlc{N=AdOv+}=k71e5wFY=KBtjpYVg^G{+c&a)> zF-07Fr+ep<{2jR-OX`OUmy9;V0$SYmbIKmD>wcuHr!`BtY9fX;_*X;ygr=i|r?8)N z;R*{EUQUHP(kzt9Pc_)M=>#aopD7^!+TxB?=N4Mj8SmP@JTUHFOBp4dkv^-Yq&%(L z@_pYGIAf%+r`Q>RnP{v{j9#nITU#5Kffoftln%UgwC>(0YF=uL{5GJE+#1EN(RV!A zE4w)7!rv#TRHN@&JEQkDlX~D?i zR?(z}Oo0?&1&wi+q&>ux2Yo)2#QvUJZEKho{~(WzCFT&zW$XxN810w>{(!Z1@<(d% z4$^hC(+-&^l3ksR`~NzM8C|$#1^h3kpwVoa|tcF1@9V zB5nOu{`F&D8rFIOuZS|h9k=(Kr{3C3D`HqEO(HIsRgzUurNb*}eP#y!_NCb}n3r`C@)YBiJXC{LyP*?N03zEUTh9aQk1U4qU^En3SoS1by-&njB^5IaU-od620l zgG97TZ&ja&CVKjTPNlR#a<(K5HHn_iFTOMTeQ!S6T~5%edvWcD*KK|*VYY1caDK+j zq_S_M%>=W(u$q=@%?I9*w45pQOMZSevkQ%sYY4Mrgf-OdDiFH|&zgvzLHC#rRCCs(&K6q0-!OU1QUtF|x$*QHI)^w3SNry5!Kn z=B4wYRlj2kDjj>&^3m+>w`o+XCE%Ul(i1OQ=-Q$fs{npH_!Z(gf8|m8twk&<(8cnu ziAlRtgb|c7Uk1rS*kxbRIb+K&xGPrGqV(m!+wm6kWt`YO6wKvPn%yH}4*7L@^GDc0 z*%J!WuMPWm7Ph0vsVE7Q+76?6u}}sp@zHf_$*f$^VSG5gIuu>;vQv@EDI`}c@gW(F zYDeq;!jG@YAS!gm+M-=oC|;qb)ymDh^e$~H{1qLZ(+RuLF=!aAtExtS3kW%+0CJ#% zj6a%=#RUb{Jr*k|D<3#b60f%FSbZZ-TU#DZ@>~llS>`!2@(u-aE$q35Fy3+lw-+mR zEwt!%lDXMDthopueLNnSQnd!Q-aC=ehk2~u0wzBq_>k<5Vc1=BLjg&;N>qDCxC4-8 zY&z2aHpwrSgI)ov>9R86k%MkrpQNFMxeL|bUruJ8wx^a-N^K>r+LMB9ZP}*M5-fKy&xGneSuG-ct#|a)PWN3eNf`P*GdjFuOntT z<;mo~G|rb9>`<&89W!;pQLbG$x55m^uYw!yfQBLL4$RIB7O-oZ^aG3h9uqI6!Zd^N z@HbRvQkqQTxvm0w5o%xS!@Nnv+b=E>yqSV*-O$uCGtdg*x+e-0`eVQoWNTS&I61w; z9#-(#UPE=%Y4~PeaB08(y2>BD>;Hr*LDnYxw%8pRVEMT6NGR^0%NDW+z?q&(5&T!XGp*qu5R?91HaL2#Gu-UzkIFAADHCeX zq-zWk4kCIgge~mr~f7kOgIHPj-UQJ;9VO zzbbp}9EpEwj2g`_t%0?Vwn8aFcD>QV0(rEN-y3tc{)szQolwU9bf2Fk9J?Vr#A`KX(2>eOLM8>NYLdb)sV4UnL+zPGyyNNk5dg}9S-;wO*GngJ2X z(;tpw9>CCxzAJJrH(xd~!((Yamzb-shta`y#@oR*El#tSY-+s@*$`6p;3CbeS9{(} zqOTK!G>8^Bx3AZKMh4Sg=CM#j2eVFrak^P^rbUq%k6*{29~8s9JjIukMDY|2RpLJ3 zrdoA%gpEoXh`eqE39lai^$h%>eLEMGd^=ZniR&k>l@EVD2Y=jGnt9FNwhj?jh|)Z- zbDx*X=cd>b<;|3vT0c*1E;mnC3u^6i2Pe!H+1~ZIV{7QV`bzuK8y#P(c=f6Uv%$@h z^!EwOFFUzDK&wZ4oBC3MG4uV>plZ({-1A94VEz&BPO3bnzVg-S71xEUVQ;^z6z&&N z6n*e0N?QX-=#BpIWY5MKyALugZQZ^tqE?CG+0I_WdM%Ee$Ku{t=q+;0w=+0havYU6 zU6F2Du?uO{?jS~$yD*-UXE{wc12HJa#fNSydEui4S0#W-F+eugHF+Jhgf z=c#@|V$arnI7%^VM}E-7y5-m0FzW;*8ZxH$RVhn2aBpp|LHQjHxDE(ziwk~Dy^Kfk zv6oD4n#EgoEM982%heBb{bQMS;acDHX%jobKYt&#rw2R~L^Ii){(MwVr{U}LS5XU0 zacW7~moiYQLvBciJ@2snHLdMtaAbScFZ8N}!^(~!CyM~G9roMrJ}CSXPdtQjYr0Mf zM3M1N>0F;6Ea!&+*sMk8QzT)*Uo!b^$2s_ik*T$#wX%FZcf2=4RfSrV$K7(VeXGT2 zvjV>mDt~$$vrm+&M8zZNk=12f=l?*D4R|~|97L$@!@7y2@sb{KyCqJqiAW7Evd!1+#o>i$lTC{=(0Cxkm-$H0k7?%Vzk93 zn4!j19NsCAwQGw zQ8W4BX{OW?Uz{hcW(V(X_>;3Hi!cg*gcf)M%3IBhCwk7Al<4X_#fHFkXf=LZXZspnVK zT&I&Z^okyZM#*Yq`=l83XjWq`c_qzK4-XPiSdxlsg4`=-8B;P$c&nN4Z``VBG1j3- z^>!37Q7Tw{MwMQ&5044hiF(Sf)a3_5rWoGYU08)K%TL|WXM6Q$suvv0&Yla<`8vFD z|NV)en^C{r{o_^y)iUoc8;Mhi08;b`vP*0xY6*J&(rG^Aeb`apQkj*zLUy-Fb_w`Zq|cK| z#H?9>z(h_$R(MjsY3CQ*_3+VZsoao?dF5xSk#9C+>1jQYR97iX#BYa%9~tQL1yrb&w8c)**;73v0MvR(?0z+ z3SR?uH|89_*u95&7g5Y0Rg*;!z7GKF#Bh8xm55>$rbY-|rds<|{^Q6Z&E?=A9Ek0R83geZ&gY-&Q&FrIOb- zMOe8P=s0QHlRll#1~O4RmP+p{Q7}$jjy^G%^$qw4#Knu!wx*71VMA~Cu;~NFL)_A< z9ZRWrw>-VVHLImzPuu=`VwR8GXkCak#=GH;P&8H;HPh@4fblJ1e*3!f9~wL?;RLTC z&_|=YKeU=3@Dbo(ocIa!iQ$@gE_NrsD1muHQ=zOF#5CuzhQcB}fz9oG?3p}no{mAq z;G71!*Y^|DOSr6Odn zRkr%oU=48FQr=#7f5ivqG*#nzjN}d!Bp0P@1zcHk9{A7Hx!w?Dgjy01i0Y@yBiy0$ z24L(B*ac&RnRphe!%VZ@LLSxw6w=@}Ltaebx?b3N$eJuEj*W}!$R9LXGGQP~o9`<9 zR)NP%&7xY-0aDcg#dA|rZnnz!eg2QE)#=!PwM)Q34y4>M#N13NKD_jkC@9GSxaav} zwcK%vKu>$Vit||NN{Rcd)H(Q8kV`s`+p;~$wF7>GWPxis2ZFsGk0@#gIo`?`;k8X< za_OZQnlcW2$TW>>XlN4ZnE!stiEcwbdMfNw=6bZhAE2%hy2cNGS)~&tpe_kWnQVYJ zd*sYe27=6f(u~^5uB;KEtA(gAw+V)NgvMFpvSa0xpeT(=GM9^5MqMY^@w!4Bs+MrhE@6`Dy8-Di2eUUqnJ%Z}m2l*$pa zGrZJg#KQ3Q%%3BlKvgSOI7@WfdHN1V%y6V1gp)nfAf(Rd$n8MsJ~tx+aInELJQ#G~ zgC2e~%7INP zjxI{l2B|7%-9HQU3Q8ZIUYVai+1l4;+i%G=tIxPO?H7;iKQw1PHToAKsF9)#)8FGE#JYOwlA-H-}{0WEe;0iN``Da{2 znGOSg(CdmJV{v7BI3hd?PR6rN)Pv=E_UqhcI}rVW5CMGf*w;tct|*Fr4zu;$Ht^nI z(|d(Z$ch_CSuy*+rXtQFT+Wz2ZCAY_42cXPKg>PY(Fj2M(RbtzR0MtCDgyDW(bA{Z ztC?*W7%XhqOou|B&!$fb>)idz_P_u@22FgeFpElf^$g@@5)=$hJ0nFFS|XrMGxr{> zmQ<`HXrW9;YKtZx7#Uh&B`-snKoKuzF8{_a{D2PKlfNaD5WP3sw9d*Oo6RV2jdsq-#55!+4=hzG1~WG0=M^MGtF2v^DhqP37CQr`4~%+Ggpr!&3l0en$rw^ow?ao7 zLgSz8bGUWM>80U0eTb>A;UPO?NhkS;6}PQ>2rNdWT~~NVx2q-@F#p1%he-0}KOM2& zU~~@9@?*CHRR9@KI?pS>5ei+aM^{tvXK}k^mVHALAS~)YIly>ayMLu8&~tu7a=vvodSkb=`Qc5; zokQfLjxNtd@9eT)IUC$*eS^;dLY|8=)0e+-BI`}-&0IgAP4eU&L<}4|2zhHJLo5w38khHfJ23q9LgYSQWz2Cm0}_u+}) z=1u+!V=*rM#h3bDtYUiKdbIr|-$=M#;RzG-5lBGAEAIVuZPNTR3J>-~(E29Ef&rfY z84B_DR?a7ZEwPbzr}SyV3v5-RXq)a_EcEH=>asg5UZQ*3uInl|Q>7##M!;Bc+XJh> z$w`HwVaJlv=430Ks1!Nut|NjQYyaAGRn2eg@yhscQZWEU8`$6WLEIByd1BfA-k`N%xbD52C{e$0-})90p8d9ZT4u==5Y8JTv^=*>BRah#MK1$ygMn5J&g!G z%3hm4v0PsYZ|&{1u@=L(tBo=vEsNFB*GvogkMWUK`&aZymA=z;b8(Gx!^QOX!EKWB z=>84)kLmT*bXe1rL;@7Iiy6;MOgbHP*N^CiPT_w$qUR;v`V?O6Ph$8^9uPOOX3p=l z#aZt}&YM2cOI~}psMux)o@`^I z$_k*12U&v`Ql5RgQ|H(f`1*XSkNr+j+Tx@qIS9uMH`ado3Kr~}sqAmVc}>e^jUjT`0%68`g?NXV-av~UKHK!oXV=H6>nC=0%FlOm{fM0U*gq>qTLjW7VS_{+eYf;pe8&R4}3*o4P&*A5gAj~uLJh|Q( zw?zcp*e5zUb*)SBP#XJKfwMz$A2WL&S#8-va`;GuUlM`qzo|R126dqkkuqL_jJC5X#SxWY2u4$1Aw^C8Pv;-kZLg z_>^q`&Nmm=`6^oQSMh;31syn0Q0X+rw%Xct&+UWMYU@4%A&`&2dde`gAyQk^Mba!I+rIO0&r%{)l^Fq+EFTwHF2R5w+p!u&SU$&Z0MzS{@QRAdsfUi@;Y`viZ$;MjCa-C}|ec+{j z4^#r=LhPi~uMHZVRqIeiK(GBD$so^^i=wIe&3sv^WG)Y^!cVO_6<5tXBGtyJUsaf^ z$;Z~tVLNqz2E~6I;uxe6%Z3Tqew$nKi>t+MZBonMVzZ0A49c!A&%X5E8BWswCeEh#qzq1< zjp6)f-etPG^_Qp$?!{UdRmTaPU2|JZLWs%m)@WR`V&5ed6E zS4Dp?eD2%(&GW4-4w5t`6+-ZN89&idKZ)L4Psx-j$IWhygY zSKp014bruDzvbtl+IZ8zNcWJ|g(Ao46_6*wV~~@CUB2zP1*{a$ywcv9MWl=K#0CK*vM+_IIE(n%KfzdDIajt>20(uw?k5W z9NkHFA)hzMIlU~|yRwi3L0n1!dAeDq1)^{zGNm}87N7uBo%QZJ4@nMi1BDH~|YsbVA2VK1kU*8s8! z0J98u&hwB45p+%V?)3X#nbgbbmxGL=Y3SLpa~AbkZ%WO0tM7`?DaRnRgY=0;FJ>V7 zCW7~jsl{J6{q8oefvtMsfap>E)BRyu`jKX&jYU`z4$!FKA9BFB`iKq4X+$xm@GpIJ z8N74QPGKS9Dzx^`T6$1Hrv1i5*%MRK09a(ycA$tAYH)lNQ2DhFFd5TZ*l#2 z=TUXQZIJXLp@?SZSX4ewejWV(!9KpIePw56My1q@*0?t~-)0K?NYji{xEzBk=Y_ml z7@#-TtTzk-t#e&wPQZSP#)Z^ryqYjF>;F@+POnteH?a!xeYtr8kIqrx42DEme zbM#UX&*WZNtj5xn4-~s+(VnF}HS+35Qt7qJ2gh#&GSpa|RKJo7MjdxWMKyQYw5bEg z@tR>j3PB)%a-d_bBQO4j_Qnj# z?031pUoLsZ^;10FAXl1Dy>xzbI9gS^H|K+>qxYWZecgV4$e&Y+vPOuzjv_icd0kIY zs^p91>Cj_w8pbfuOeND)PfDfUa-BS*tXiIG;BOO!AQLzeKHf%!C}JcOq3hEg+XtUL zf-KI#>P>NIfJB_K4T6GPw+tM6TmWWzZWq&M! z`f0kXxd15HWOp|z;|*Y-WDdVJovTOzalG_BaebXeG@~XB06{V7h>7~%e$k_&Zz!{B zl*uXZqt{mXCj6cq2XMumnYYr$Ijd>c-ecfH%)v{*?nsMUwQygFo3ICEs?|R@`~Ri1 zy6LCI)su6{#(jde8bE&S%5`8tpVpDrh^bUh;dIv)($*xt%d5c*2MdZi3`b8+9gaGnu5Ru`TMrI>Xv>K{lXAJ>Py_2e zt)WF&&<)totTS4PA!n({(H-w=d#fabMGGdz1xVrEvm5(kQ9gRj&Mn4nr3qv!w!~1< zxMbwz8JKdiq-1T`h4$LFM7lEAN4T!`gVHAGVV$}n-Lnke!ZCx_}!CCXtH%BTj zg$h@`umZ|+6+l$Go?1+n9p&cR@qXzsS+@nS>80{WEsg4S$&;E zaw`$)G+l zy}>oYWos*gct>m0EOXBN`FkK+o3&>Sse3gQ59V{8%U#KLsE$la`CMWVQ=r4C(}kig zsWOh#vuJE}&&xT*OmW~I%Pn_Q#BrXIF=ieO@YL5SFyI6DSjp2f9AX4O6ydBranB@! zj4SU;xq09aO}D3DMVty@8Pkg1`l01|$~7u$k%BO^lyQQIT5RUkH=%(vGB`%}?0E8K zF7%{on@D*zO@AKjXIZjo`yaGRxC5ggMFVsb9$CbFyg~dqs|7fxbUM6PC}$|IVUYp2 zzljU#{s8QxE1of&UOINjb5Ys-1?4xu#WTDNF}$WP3<@K+3?J z03JZr{#^XawAMB@jTma;Kp5wrR2MI|rDk^aI+!8f&+L-8WaA7EpYH;V8|ltQ8ViR# zG6~>bxm}~W%}lv+!Z))d_M5e@=s0CK9oqwR9fm6`{>3lZ&uxqlI}lSpr~k+{ieYLl z30IcWt}VZ>+!U8^G+V})!`<2&BW7i>#~EJ89pg$Uh}{a+hb8)hWl@ZF-33T++6#7KmTj^ztyU{JryAmn-(D zc39ayr8QM3XxWCD)L!Z8LR{ICokj84@Zp7W?&Jm6*{rZW-pC5WzX<%6%Y~Q!48ZWz zRGY4OeU>RT>5BFkkC>O(yOHqyBl|iN@_zO?21fKGow3Ew5X1y7P;sYQz|p`am@NWmgZKCeyh z+yOHVaKQbn9FVe87;gSbrNc5N&ya4>d z4kN`(8@rPMT@=2 zX5joavHeFl{u=-9T2m0^JrPG72j4NY2w^Aii-zouxe1p~?@pyp zzS_FgrPiFap~{aMu{Erw759)YUOlVQ_3RY@dgM`i6gNwM z%FM@LwkbieU{y{ZEy&hd)~Gcu)n{dk1X)LjdyqHORfjrvR;1II<&27lB5r+qjQGdk zICT5RG9+(NjOl`P*yj-cK5&wbLxYf(I~S4M*dqGoHHvL;jFe332K5GXM9&6$nE3AS zR03SEIug>^VMqtz<-vs^sOFe_^6EuC*q~(Epf>*z1ToMAAwj~C$9MKiav!Dq zANMr%nb4%2dz-{mwyUJ@R1F{Xl%1(Sn$1A;hT|q`k7TPKUu;4z6Qa&9Vg%aztbucR z^H_N6ceoa?RyK8<-TdCHGqIabqLhsd3VIj$u$P-aWD40oLjP<) zgEkBE$^SQ#7Niyv*E{ziM=X~HG@Erv?CyVhQj_kSa^E|3@(mqTSf6rTsM<`2 zt*=7n=`9fLK`~Fa3-Q9cF zJ!pNaUM>|EXuI2|=Zz-u(xC_mrppHxi+3+|G!j7DKv;BsXQY|F0uuOTP4BQ=&`LCB zoRJw|R1-uf_k=WJHl}qUFfzA(S;>CMyDz2zyi`>?a$Nj(+}!2@?6>bSk*qxT5zLbQ)^`v>0N7yyZcO&eTIVewK}h5LukLkK`3085pN-6 zsS2jZmcbXj3ScL$twXLrKx)Srl$F=+WZu*5r_Oz|w=^N_Jz1YK>j0cUbJ8Vs%TKut z%GC=@svZ1z_O&32r38sEog|ZQ=$yq&&<>Sz%kU11NVatZC=y`khp3|9g2;aO)G!+e z-BwS?SLiT%?AL2#at{DYXvTo8?pSuzLu_(%@}8wV{3%*46Z4iX8Tpx z1XE4kP(Sfb0$%`IF4q2ykESj;lTv)N4GpD2+GvM?&X6zN^GsW}IIpJU_y8=$;y$#J zv78^h3t!A_!EAKeWSgeG<9QBM4YmKO`Y5i|AGG9v(8l*$H3J_GYBQnteV@CM@+0<= z`3-l))-G-SAf&cD)j&rBpH<>E=d35ZoX6it_uCmhe83)PBC_ewX_37L*z8)n$<}lI zPt7KNBvtHB!=vDH2eeiXDI8SyDI;-w85!DJ4>_-@x($R*>ixr|ekudxGTzdAd!uNXkaBpl*xwU- zbLAX;OjwxYv~bOS-gt~in5&Czb>D;)2`Ge3)Au2?TJE>nW{9Bf0IU<+B+iv#-Sp6U zdq~-OgHde`<5I?Q15*3`)rbdZ+~0&D__^e;@pvP1U&1%b>+f?nEw{s2N4q5Qm45-f zkfDXLK0oz<7x_A5(?&VCUolmI0t|RUuPHEP87VLk2Q^eqWLLVBgYxh$i@Z&+h6=99 zPMo+aVVzKPhuYMXcV@RpbFQB*Vld?M;2%)D`gU%gqS`mzP*9xcENHmE=|o=LCCC^` zLGg?G^JS2R-v<5pd+LF=Wp?a>%%?6Y0u?b&AEjjCHyda%TYM3;RNwy%ZpUI)%Vshd z%Wk&nTC^2CYc4yY?V_Hep`&QLM8LT+I7Vmcw;`>kVd)8Gx z(q~LE48@e%=XT;c^Zp3Z4M=vUc>E{gpb?egG5XtY;|9g!zhe(8o&!i!n8K~E$H>704n6roVj)HKyQv|-=5@R*7h8^~m(wVJ`rVyI3pkSXhW6>>LM8nfXMEYzX*@S=N{f0f)^`IOSalZW;LFr&+YBTb1Rv!fR3wa-F*kjM?K@sEqIbaDwmb6 zmCEdQL9$v~z_OEUa0!2W~U+pIFl5Zvu~jusnR4RTV0!as8o1%;y1FR=)G zbs(8Sd~Lcv0D-joD)q~}_$o7cEQ0oI^dl`}EjCaHixgfuXqLY6Cd|+iDTS}NSI{0g zA$eALV8F)TTUZmO&(_D*ngP8FG23|XE*RAcS=uVYMU`=#f7oY(H=*cvKrTab51=?i zmV?f&QbQk3WkGv}oJk50KylS4CVK}W0yhVkhqhA#Nf||34uI-gHTWg|cF*B`8Ff2N z;XQa4q?`SXSL$qwIaM)hF#oh$bErw>i5lS@uGt*;BFQ!*U?Ct|`feVN8x#@uSDMV+ z{ks@mfVig*Cc*4QjPC4OKrCYj%)x7SH=GYjL{RbTo&Sq@dgOj|uamPwj1%gym@K&M zvaT9a_l3F(%A#5^uYMCKUfEyr;TvuX14fj;W72L9qgw|#Iz$);serbxwd*DKm z%YU}EtAKs^@!YT4 z$n?l%{pFyIn_Dr+?%;^rx>TLv+^O4SQxzI$$Z;msF=mTLeDDwGF0qY+*hY?$*M6Hp zK}sOMFTnVbS^QdBye779UsSy>L@(pT&PJq+%deCv`(kDdwD z)fpw2=re{A@?%~`*TN7c7xL8y;+D!Yp;=LvRoy7CdI(e z>yLXakCYyP_QHYaMoys-zl#70ie2&}UaQ+r7Rypt-<8QA{!QP7rsQIa<^XZ`Oc>Pg zU-{zDiRdH|ondW6cAj;FfBcbp!dgQA&&%X+Lg5gdTo4Uh7_d54UFG0$*MsvQ$vPv0 zbMdP`p8kpXjloJ~)>B zGc_O1Dtn=8nwevgqtdCoGfgWl8dxi&X3DKr$P?wtkcZgnBOx93uG^mw!D=~QyT;~8 zC!kzqZED#v%_k5Vh~j|^h8@U!4)VkM1^q{-;6`4a?=_78S{SyL1Mm|^&{)fD)In2> z)A)3UA*@L98WeB8tpDLZ+xIG4M*n)5Z!q$ENq&=Z6^L{o@xIKE%z%oPKWXsUVorKt z_I&W9vdv<9=h&jw^exY>VA}Kw!#IPRfMN^g81z@A|M@e)_byntz^}ij!a!LGoQHjL zCWwk3_dRA{i%uf*5|1~pXRD>4RvhY2^#=P?^IPr8XW{fb-&L&r?^k}G84L|_ zSyauBE^Lbuv~`*6Eh-rt&rO9H>oAp7vS2<)8B_0-=b(|KX2(=kfhRTQW{Y(T$8kl_r6=Y0jqBJs21NVt?fSjFuQe}G2Pj7gUxtTO&b%+mS?vDF zXusJfnq7cM_?!h2>mh4D$9$pM1h5f6vJbS221qA+$WzhqdFa$VEBZ(#YdLs4oNWkU zp2&jBdjc=mN-0o<2u+EDV6^Ys1eti?S4$jNS90-iEdbQB8F_}*c{=WRto?xRHB)Tn zq7b~COAD%AfA_ZDS^2SSR_Rp!cPnKn+3;nN-z|&Sl|1HcvZe)n04zvSw=FJQ*=6LjsiRP z&2Li~_^@xJc=KaFiGNfFZagsIPYLoDQ34n>4+hK}r)kTU@zI&nU5v@pjS7W3^t5NV zET*4c!H1z#f5LSHly-7@$=B%gq~NB+wU2=FQ=erx!k zo!yB%{Q*QJSzxeqG2+|W-(VkHOLk(Dz8wEDP*&;kaP$EOS7G<%hi1IOXJEz#JnoVw zCx?f%$(gmb_zv~xw(^`Vp$EJ{Vd46FM@*kh481Y$T1k5HbaDDcd@(VXR=hO(rr||! zx2}Nmzi9LS1#k13)gCO#HMnoGF-+TpEyF73XX7`$V9wdtK1F-=pd^xCu25}x9ypv$ zC$05Ljcte*txpvd#Ud}`=VRv*nedD$2XF({=|Q}!zKCmm*F)sx7v`` zXj{iLEcD$yM89%JeH0AsQYe(u=VtX2;nw9}tTZQTWea`nT>IlBoUdLO@=}Y8ijwEh zIQInloC9|>d_iu|g-04Je4ks&(@$NFG^di zJlC&syVWEcXPU>;*kq7X5~ett@=&~H>g~K~yzNCdZB0)S+-`tsoA9ak4~_wOB`^bM z8|rum-e>ZYF;^V;96|7G{LS`)4=QB&Q|>2u*HWBU;zqdVYDB9nj6Ag7n7M47P^JnX z9A_wyvku}ogc`tWQ4ZC|NS9WEqw8QCZ4vF}$=xBMT>T>ZMKmZv8?64na1NL>ly+b! zReV^KX9$9^u((3TgXOcyiPsTTR2vKFEga#Sna+=av;?s9;aXrmLwWCl zG!k0+drGKlWj8?}h|D)#FGN=RE5(`@bVjAA-AxacnJ-wm9TwxVv;xlA%Z~4$ zZ(QEL|AOprKfoANz`qk(!SwN9BgW9$ZsZ@9iiCYG^k{9$;mwP<0h7`lWxaKB=L z>??qa;XUvufvjEhYZ3+c^F7#qaUb0gcwm8p)KdNHYq{%6JzOdwF>Zb685un`ck9YQ zm>qWpYh3W*Lma9+(F-cYp-&wGl|;1!U@rP{U_+0KL1oEfhJUqffc@M2yPXmAQjd|{ zN#J_}!8Phr6(+xSbxQ@<)%c7pyRzOz_J}4Tb{*kn1*kfdj?zw!G|cy{o?%#M%ss24 z;OD#%x%_3FMIX$C%g?O#cP=;2oNS5AKUUVr3*zfC)F-k(p6Y*N#RRaw7yBv-G!!bK zN)2oT|G+FYidg&E#}~mJ0J=m%3qGX(mZ!N%Ip-;{+D)T~c3AJ4WGu9;EHpEh_q3IW zOP8TQ250||1KE2pF3=hjE_Hj~6QDeprIDI2`ioSL3{lWWRi&xhtc0uV(iFnVa2;|`}1(S9IH6PH(YQc>?LERapWlQppd9?pyTKBI( z-){ar$pyTQPmr%0$~mQZiLb#tu=RCi?GEu49x28nufkg1F|kdp#4x1meA~AeT!Vx1 z(iW6U4z>kJB&TPPb_|e0OXWH2?{N2}=~j;5VV8$9*Etkw^yM{0{q@VCYUQ)xS?Jhe z!1ZSl4g0tXQhk7R_7HiiciTLx+Sd23J26$x=|0~W}E{ExLt z0|^9zET^W1U(q0E`zC>kFbW-Xe^@nI&!*&$t+e?+<@1rgfHN`AYmKpgQxfuYF%uL? z+V0@Q{U~SD0RZ%T8l;Q@$@Jd>P|1RIXhk0)la@C z@jNuthJrjbih?3y?4VHyU|*`=^T)skzJ1?&w^YEkUh!)1RKx7ai9IgQz$&(@;;$fy zVB%L7^8~QKa1&+y5m@dI00I^V0joFEKYLw-}_hl$_|Qg-v<>emx~( zUj|e&SZ~;b_b!6{V!_6lgEWN7TaU|1*tvT6B9^bjT-PORvY-ROWDP}x=M}$&%_l*< z{aneO)muH{t+K3nLlu%?fe~&=vC;ZbQ}I@8Z9}s&FJxa(d>s9yZ+O2IEgd~;S^6fy zi~r@77bAqTU!Ef1lcw-Y6&Iz%P3@0qR!cM0sIF~lyJbn?`aDAO_TSIMK*b)EDf)9n z=H;@YayswQ356$DtU;i?IhieZA>NBk7X&`YhM zXaw@D+j-|B7uqB8>*qVzBOLjRBiTUYMl2nWD3M?~8dgYgZI&||7pO;l*&abgCk_(; zm49$b)Ew!H*S@4{;r;cax?$&?)rwq2jW6A3I@^{6J1Gsg5eM|A^EFFlcQ>n{z~^p`y463LG&dG6mMqhYHVulGKq%ru`b|rU|2dZ zU3YA?n`Y-%qxu~I7|38XIyS5O8Eiub(i|AoN_4+KJh#71X0PSZ(ia0k$^&(uLCF@# zg0p|q0e4s$#kILIJNiS#dhg{z5OvNU2m$0YM&)OSt>tMXyl^OyZL!$q|C1tM8tPc; zjK&qMIO?O+MWk#;G9$C$&W+ob$9wH7jr6Z;%Nrnjwr!YCm||;(9?F>3`*{v+rzLM} zCePXD(s&Jr+V5^w1EbJiK_F3%1?~c8JZ{r{f!N?n=k8L2l%m~vH4Q}DGz$0v z!C(3Z47O{l0;LzF7?XRV>*Vk&e8y~r90L~z&7NBi1a?tI9MvIuyq)ZH#F6~w&ZC9Q;?hQVY|g#ZTyKQ9KuoOro&4?`l*Jb78TZgu+(k32MI`ZX&uwF1W9&skCLKy$jB4J;<%Z zYj>gAstTpHx}}=Ra!O=Mxi5xy6-F@(Ec5pES(hbrCUmf6^Lql+M@MNS3!_gD@wIEH zBQRW`z533LVE>$*1^tmi`$hcYM+Cl0=hmIm`@A!5tzd*nWfxx+zRBw9x+G3NsN*y$ z#q`s4y=1n2$L)z+;%W4dlJ?-mPzvD6Ss7X6n7nyTI`*M4kgJTMFR<85o zXER)ilJv9H`IV{l3zmp4Q`OTJZh2^`R5b>#?L7fOu@P#ox~JW>BIz5+b6-OjWQ>?+ zU$|rDR%YiJw0O?>@^++>F6^@YuoJuCG#WLUD793P8tAw=X6CY0k#=JpTT6V8mJa4S zTh|OQVd(lOyGb}0qoGhDYsh=`$!cdnnBA@e{{8$OT>|`~pGlO->0P@_H2p@^?f`1<@m{E|e!oNtXQa+*ZoR0T^=XEzE6w+l=Cc&|UxqsQJ|m=y z5c^u!;u<6@O9w2)&NVruyP)J;t9JsUE}A-__ZU`XdRyBjJj{j*tLWSQyxJw#`qiv< zu_>c_Lz+6+?6u-W@Gbu+h%2>r%aQ_Z-2*j=edglC0iA3dFmJSndEWaQ#VL603C}SL zVsX;$Q->RdR2Ho%Qvg$ENCoK^qqaY^!w|(V0(Bi%--ssAB*KQ*^7SYk$c^yPtTwWzo%88@6`-8S57@LrE5pVw`B-no~{4f zEV^vOD7RO0mL|&`fT`Ev{2Q(jE`$0UU@$p#k5vovg2z!oYlr79Z#n#=80}e#monZB zT}_~}cx($siD}O{o5+$P`s%OJuuLcNGQddJtD$)*$4Rk&CjS@~@BrbGn7Tx+Rp){zekGf@WYUZ^1Js%I3z2HBZ zf-|T4Z+UZ6NLQnslVWfA%rRT&Enip4jN`h>tj}sHoeH~N`#q{oI8F3Iow~^B#7CLv zLiyfRLF^`vdTR5bPF9EYqYSHGPbAuu4St=wFs-Z`OQ`i{Uui&NZ?C>obMUh)uB=78 zl(mX%J$t*A-(oiQ5-Bg9+nTDK>-i&-)F-63l?+`qqOGGG*PZ}2nXHwYm9l2$4gKY} zS9a08&*s%2ap}T{Z1SSk&{Gm>X~*(WJO57Oby75H^(iUjI#IjiTo7g#w(P<%^KwvE z<7)B3{ab}^7R#AJC8hiWq`2|eB!p0#m=i{2+AL9Od&WU|X@+C`-CCUp2ffapw6vqA z>4yGXwOiVi^U8i1qEJU8VR8o7^SHk5P^|D7KwUEmMAq_2+8Z)n)}h8K&I; zn{umFqp(Ua9j?f8si=ctz2t2mAwJT%aig03=P18EcFk)WKikzEfgVyu3VCl3$|A$x zyv%2p-uCxga@W1OHHc=XN!S>(&)fF|>?Ot(JM=~D;K#05eB0$7Cnfao+x34fs=d0P zhxcsD$g%q2PuE{u&}=5w?1r+XvwcM%GSVGBv*N};4Q;H#^<4F40f*E#Pbh_KHe1-TeFn*DeKCnUeVGkf))=7gkE(;5b*G6yEgMWD_Qu2X9wYhX4KNrwmbFx zkFd{6t<;J!VPoo`H&$iv^9m7X?I&W}NCsUo% zJ4vln{I+4}()lM|*aPdm<2-9x9%(yIzsa|<$Uxiho9{es6)tMth+|eNC>8%QYo0o2 z$VS6Wx;`N->DT^lJv%11<5HAK^d^HkL&dfi@NbVai)Ye@yML-&6hAp<&lPEouegnx ztNANqrnxWJniVf*-=w-$l`~Uz%iuF%BXp@x!!)||%{1}l2`=P&{;Bxb2f1$UCLEDx zO7bSeNthSzQUYeNgC6j9b(#R#W6jx?DaOp&ktP9bXeJ$XUIv|lzFqob&BdX!*tC~w z*-YES9!tN15h{T^vx)d)$iD5^F#IK(bpCaZ?GZ`?@5RP3TUqb1l_EXtn%g$bx~q`I z1E+1eUDu1-z90BWJ=Y0u%BQa3Nu4Tu>!u3%H|e)=^T+K)HvEV$_v(guB99y6MEFD6MF6u_rxP3AH#&tI{xK+d+61K8xzNW4&-T{^pgM_Twxsu~Kc%H4~LwvHe{wB4_-;0zNt6&7@jh8z*r?9Ck^9 z@sbL~>EeS%&RRh*Z2i)Nw5A}(&{t2xVA&$oh&`nFH|5tykCA%Yu0CrVxVCet=jITaL{5$^Xf9fRc9g>_y*9<(D1eeiu<-@^eUS`pU?})0x0@FNs~cEmt9o zO<=NcEala2mwJtNL$5yE(f5`a-}x{y(^CBII!5{>B~$?9O&~x-x7Tl01`&#r?a+258Szy2190t!yY7sP^RB_>Dsgm2iHu+*nevOL(;bJde z|CvGhfGA#U0CP`3(Qyq9ZYNC{G3I_HLIuctbUVD`l%NF=D_TG~{HgcWfd-~%_Yc3D z(FgVCuJ`i3UJ+0lv~(GcXj6?XJvH6G@G~WL@=04)oX$v4%;sr9sV z8!cVl5-GoTsSMt{@ZfpNv&`$nwFyT5cP&Be(Y8i0EWND^XJY#bKe-*9K4XX9j1GQH z`4{O-^7(9%eo0+G*SDDZPnA9?J{e`%>byhJ>=Xt7c;+vp4;t1#eArsGiu`7IWbKeH zW@_r|R4UyRbAa&6D{)uotTTH5zMT?&zm!zKKF^n46z)3Z@aEp7%$DT~zQ>JSwkg{d z=2CjtrDS)S^sw$uraoLeY@Xg)x=-tP8W~=di|fN|7*$8k70ECpe994+43N0eX51Mk z;k0QbBDYaXH|xnftcXBINQg~u<6N?-mo*&lwu#I%3GIA_N16jWo(Q$PzW4lwFyV@k zw+KvWpl?Hv+Kg~LcuxW5t%2%sTTGvSgknZw@4|!Lb(m5LzG-o_u z)9MbZOkeVyoVs#y=>jk>{zFJr1@6W6#fRb*MR(V^foP8bTo_JtCixBtP;)pGs#KR?Fp55M5|poC%lyIN#2)kXAlu3=L0 z%#i*zOQge%Dp_nLS71wJ@gEj9cLgL!6_2hu&r`lwz9Z`?cdXIJbd4qYMlwtBHH^I; zn&2g8hG!1&uOC?3u`AxTQ`qvShTi#c1S-~ouJA^G4@{v{!wmjwyf z)^(n{Jz_$5Fe)%aNWLUsbhE`Q?}+ITfASE2yW&Ixd+7J1qFv(&zq~@qQ2+Oy;HcvY z!-Qh3jUJ__h!kQn8ljR7Q(i2pm~DxhzpyaZXG z>w${?duQR}+u$Xtv&FnBQHOiLu`HfZtJ zI-{>f8qhC(=VI3-ndD4%du%&0wpN{Wv}z#kUMRQb`Itkr%p%NUSAK}axTg!hbj{Sw z(K&rtB{jaxAV+@9^?Zi^d)?-Rryw57)3@oRzR8Avub`50s}bHt`hyaB#pd)8vK=dp zzQ1}D$CIU!i~OkQdeq2uPmxINaeN5!&v_r*PJ36-tp zclb)Z>mo_`{-ze@@*$V)sCuO!d3a{qQ?*K%&-dWfOW3>@$a6i&d#kjMDD`B-PTVVL zjm7tOAy1ASraT|Y+!E3SV9mmROD$M0A~ zuREJzfQTS!faLd*%y?m4{Gi80|AuFds*-?XSNo$Ti_hZ87iE_%jBOv71#DUG;M0_x zTgSS3+Y&^c;7PMT#XU&ARI@ACv{#>J0`K*9jo`R2c`4O5eU_{o%a{Yjv`tc^+@xl; zLFb1Vp*xK4)y1a|3-a*Y8~q?7yH{ScO)>PxSnPgW;DuwB5?y!d%;qHdtodG)52aeM zc12FkGg{YRSZmX{xvmSeRw;5j8)3_09dI>71C4uTqM1&TFXP|xCz(-cSp-)m?=mZ4 zeBUrL-DN|7i~3}Gb0<84Uy=G=o5k~os4#`2(w2Q2`0p^Y?(0bxnthU!i56h!47^TO zS6MwJ=8|psmlnZGE0ZmhtAG6#wt$Gsb?#5*z}rve_24Xk<2xJcraRLTS;{D0ZfyD- zx&`8)@V<5rE*_KS!B3YAQjaCMyF?}`dFWu3Fdk7E^8ArL4K345cCMwA)5)q8D;|v7 zUr)rPcf4mKRtMPAsz1bAM}~xdX5G~nn9a~{V*c1vemtCK2QXy!fVwP zrF6U8Q3>-#z)-Em)=Bj0g`2X!$;@R#EM4Or+n(2QvV}y1CZIJuP;$yWvhV@E0Yb@V z!31zV)lGAQ`0xfO(YZ$fyT+p$SzSd#qt%KC&z-8OuS2Y%FWM}2OIseu9VOkNCP;w4 z8MrQ=`VRE~mzfwdG8_}4FupL#ew{ufFke<7J#5g{TJDqXR8+4Pon;nk@pu2VF5XbP{YeSc1NAG`bGAeBpq*s*Qp>xU2mF zr*hofh9jH(yGcyHSo0cgypF|Dq?;#{-mc`%fJO?*}gC{m_vxW_*R?sv)&$+;s82+K7N$pwG=gZL*aZ8i;KgFOZzdNcd`@UOH zL;O5tjPdrocy(d)X@O~?X6yx@YUJ9jpveqAOyS#g*pd~SpNU7$Tzwl0Lh;q2b9mf$ zjSvtd)q#X!BFzV)9en%yy^e-zs%Jx1#dKrQ267!r`c!6AXh3z!xECXhh0yaKfw?BV zG~6{uc*857G;YPJAsQT+afYYK|AZ;dI&Y%*dr}zN!?$_1SG8DEEAqYRXz%n$yZXx} z%QQO#5*UqvPM3&QiLqR5GwY_QspWWQpTk_s4*2$rij|tuoEgsf=0WTDKJQs!5FkxB z)Xv)X;5IO#7PyFR@-ZvHWatz9B))vmo-slb?tJZGMbHE@Gs&Sb8-j!>*f%<+UsCz) z?bj2<1@5OwNRej9w#ZwGD8V*24iP-3CHuM9qyo+iQK}$LA?Oe=&{wIliukke|ov*<3U+alX9Hk<7*OZP-*$6KPscr=|_%D zSJn%)xK)=w>g0q{37w|^kDOq^n6{OC|Do(y+;4F#w`4NCo|sLP2hDfpG#->|#u^Vj zK7>0LX{hnK2E6U{sdWbM!u(NQ?pj7&zxblJO4e{y#2JjBV1KqlaY>TdtTgs7gwXc7$`3w&q(p@@2x5h;9zMv+7@pQp>@-ERd0rbsE}cmH$k(dM}|@;p^33NeXbgM*`^i zV0Ri1ws%4DEnge2c=%KqiJ+wz@oP=lU^Xj%62fPQli9wYtX)DQn2<0kbjO-u?s?RN zhq^c!(1ZjS4c3^geDoNVxfoCl@Qlk#v)8W`Ks6iWVWg3tR_!st+tM!03?@Daovi9B z3oSPDEZ=tP+O*~y{#ASyMMD`6>N0|(WlTL-hzK_nZ|lk3J=zw7C_Ye=+Nh96Kc(*T zxquqD&zM~-|LE4&uFT*!>iEal`PpYt!Sk&W>2ZqEPTcjH~XX0 z-DW(0R~imnP&Bu>Bn-OfntbRh;Jkc^glFl#feSG?m%takDpWUjkq#QDzcUMvATCR0 z!fX8JrItAQD&xeK?GyrEdP%rzgHPI}S1fM;F7~IJs^CtVSbk1=`joV@eH_Jaerd9& zJ~hCxqAqnm?q1*M-xksMd67OD&~LxD5u>WHf-)T0pOB`K}>+~ zik@ZZ1Ps5LD=9DPY+-!3`TPZigRUMLJk(fdEh&gB_->p2o+PwUmYcs&EG?}vSRgqZ zS#@WBY9Vtv2R+e#%p^e{gW!o)6ZP&s_VwcGs&d?FI@A< zo?}-`==vkL)P%>1fyN8u>Aas89+bU1NS2C2w8b{VZdR0oi?ex!EB zSbRfDE{NoZPqAd>vj0?87_y(G!v~+DiyiW~n*c})J`8jD2(NW5Mr5TcYOTnYRad0o zjC4Olihj4z@f<>yHE-`xFRDmo)|t%K_{bS3_}#?@1@Jl^R=oeqx$$Z%kIP*Re$~Os zA4T_}##4h#fqbq^X^>hkUW|D7xukx_;Q3Dd<5RIKHa7-88C_gQbbo6WA76JrLGtci z>`xO;c@6xdrc0!E{8J?iEz8$)lLzoM&Mf0%9cw#o5-A>}tuQBV+Y{K?(UTp%RxMzX zGWEdM`;{;8=o2q@^Tz?uH(Aa*fVB87%PirHw*s$ZG^IJ15 zX<2H#rtV91wmUmczBE^w$Y|;HvE(P-&j<|a#8Zc>ken7yEMEgRE51ydAL?em3bw>M zwgu;P&xWr%l?!p51_ka;x8koib~yt@r`tDO&VX*Xz}@JI4E^g?fTey^B-f|!%qdPx zbcd+)1?3#wmFX=oq6r;MzZQ0#@esN3foo-B*-Tu|Z}_!=J_=mzq}*o9>~C5yj4w@j zTMxu>sHOSNcxGO%a#ap8x>hG2nP>*&9hNv?Q*7!7Rt34w%4(3VBlQm&YIxRO&pw_x zh1t=nR4j46*Hdm~geK{27Au3!fU)yM%4`Mh8)DxqT?fY|o2*9KVz95H7Cy1w&iSTV zQCyHDj6t?xKZ(Z*1mi>;+moqH7w>r#(J$f_cyHTQUaqM(GGO+Y2KpZk74?dR(uT7#o;f7Pfq7@LoNZRzIR+WF8-Qeins5>v?I;*z|A$ktZOH@mu1 zCwQ_e{JPh`lQtd%=ds2UM3Oh&{>Q1~9g-%r-=<&7YPEhmVGlQN9Uk*iT-`7NcznVd zPf%2TvDtWBek-}kknz+}^~b9Zm+Hc9oTV<7(EDbatDZ?&m2U-D#p8umjsV+?M&4%( zjm#%lm#oTf;a)d=^Swxr#-cNxI=jg-F0DIW3~xqbQ`4=U$x@xAx3M+Zv)yW+%oZ>u zf*PQ;%Dz${uz0hkOBK<^v~xOTvvjYRGcQll5`fg3MnBGk@o}4_TaBEmn-SYy)*%Ew zBXrjj$CRt3m4%dFo-MbQ9(ewY_;Vy->bAQ21H%p4;y?C|lTyY=n?r{u&XDZo%}&qU z!N2o{n~$I)Pjwi>s&4!g`=pCKv*N=r2c)5JVlGoyI4(@(bCcm#|0nz?6WE*1&?w1q z=UV)NsD-KJJ3G}OuEeJU74G4F{@>GzFb+awhZn{9Adup<9h%xKuu0nt#9a%kl zN*mJ=J6yKu;??%|ehtq2_dc->CQ0B#PSQSBF~(GQIISckDErq?phyuAup$;jQ3x#{BE3nM62O8e7_b2X0wTSJ8XzG=K|w`& zCnTY_00BZ0AS8JwL3g{i_p{G?zC0h!|MJ3=$&@uSYu2ptTfwGA$Lm$g{R>mt&c60n zIAsMLl#lx2cirGk)TXJA1+wLDwkTOZ&#Ac{J>bYou(3|PXt_XX$L-UPv)+Y!GxuV6 zu>_aqnHtjPa6}V*PIYQuIj|hxzVu}{rf*QVF7bYcUQz5M^2iZ?!7YP|kW!GLTi)ts zRi^V+T$8@Ro~RT*~?WPwM@O+X`y;#R?LhqXrtzG#}^A4NRfPLPgI#MmWX zBm}P~@AYoHL>S17nkD?^qmf$0g4w_OiuzEu-K17kPUeMPoVf{})$3IuSh*S@?5^-o z-MohQTej7s9_u@rN(;;3#GMV#v)3ISeEK=HC5chtqnD#1PMXqa1FjW>X-NFjh%@JI zOMRHVP~)xCcn%;XNCuLa&gD1kYC{NYaaV8|y}07xc$VW63p|=%)DhVSWQYNBQ_9Y~ zwI3qt$-Y0rKK20LSdN$X`M?X_jI9nAl+Vv~3}3pLZF~V@ew(8}82GM0_`~@>T0vSZ za|853>&S&U;4UQjYRG5K&GyqTb;Emfij{!^Sowf|dj>~G@~UcUrhUfCny-ABd?{b< z$BbC&Nnd>9s&{z8e6K~Hn^s$vh{T1jX~Ts#oo#u8Z{9F^`|XPom9D6Q)&`FGMT=F7 zeb{p^o}aL}>A6WN++aZDlwoE$8=3xfd1$8d#DcLzmWa<*6Wif}CS)4yH<9~|K7)}; z;}y8Qxk86hjx(iVEM)xE8P7#ar5_8J+>^Ut?H_f>$L2o5yxPakIpbMNRojQo=8C7# zehVGK{4|&?1Pw!;W z^H|L8K9(l9z{p!^Ux4g&`_kI?_Lt+Y9z1CYNA3#P^WuWM-N^YD;#V1P7BkSU-^~YC z$af>ts-@J)qJhLr9hlW<$dug1dVup=e5r6S?}S`#609f2^n#Cz8@p_%TV)PuC1Hyu zKdqr`YjZT(q61Y{(9mHWhRVi87ld8}l&-i#k-N~!s9pUbF}K;UXOKS*CoEzO#e_z5cS{mC}L_vyI{m~ zN~Y+Q(ES(%Z=XAAo^Fd;Gd5(cOEW%)UjSu6oaMOC@Dx35lc~}`BlB5g$NdYw*Q^7P zrb;Ql)F#=?ll2J^m=HGn$PB?!L$`+PK9+KxWAd!;qiBv4=5+#NmpZF`S#&R^R9n7{ zvdy1uw}}P6DySVj)Z`bj3pWtaN_=vpU;4zASX`h)EMl!hD_*I*ck?pTOoWBHp0G=r z8=)hHow2?_xxL;(mbZItk-Yde8aA4mDaub2RpWP|Mq2a zX#T@2QkY^`i?dFfY>NLV`FD-8Db9T+z*JYzJNY}7xi(17v)^0rlJ&&|N9ZfXt1bA*H%_lSBGLr8J_9fE zf7irrmR@oe$SG7fe$DnFa5~caDth@h;ZJAn5noXk9r`OiJQ+tG-S4b&@bH1^eIa%) z6m&z|hcwc0zuIr~S9QGg&dF@)MtFqh6|7y?&?k!vFQSz*F9bB~EAZ^diHgqMHoIr< z!iiycvFc5m_>_9#{zY-^7vH*E%`_#C+TO5&rWT!6`qk)~s@{v+0sZUudhwho{+=6} zVyGK7B=f7BH+*wR>`OeKmVqoSkSRK8DN0U9-ye)kl74AtdF3PdfX7wN0UBsGb4Oy- z@Q <${;ENeKxRytmfyVu6@@eM{7VUh-&sq-SJ9S{o~}cde|1q`hJpKMlJd=-}gK zJ?`Gy_exn&`lAXf9<}Gw6{7T>%JaVjCcIg8UK?o8Mt*llCAd;aFQ%O~DwkSS@$n8= zb7C5mS2ui~TaLiwhzx;y4sd|OFN^X5M~Ek|Yfi7Mpr5*lAxt0Z9{WsT)L8{BfGf1s z=sdi9YtD$eR$|}XgTuv4zWg<&Pt#9p`IEg1>a&65p)IMG$g0Yq%tqz)oly9|td}g2 zihB*zxgTI=3CXj_P+=)Z7h(=^w_IT3++0~agrQ$MIM{c0w5)aX*2m6c!r1etM9-XE z{htn`(Vn_fs=vi#Q=Yozg&vxizOffi<<%?9)iyEau-mOG(AN@w=xdMc!#D}nObDGZ zII+I&*vs^~`}2Huo=f!FU!pxD3FOd6xrST59&6Qzthk!={>#JcXO*&cvZe|isT-s& z$V;g}V$Zy6>~*|e-pm&6y5ey?JLs20_VfMDsO=Sag?jfCgI$-he~nycdifS*$u(nC z?WWNNSHrfid+JgbZVg?C1h<+UFeg31O4R3P^N6u)!?)cef+W8~0pQ0bDmM4(2G|;T zUVC{#uKwk#_luuje$MTkX1|%VG<7aY-sk`6-G|GuafkFrc!1)yvyTd#j%+1HL#K|X zX}+|U72YXP-osfP8>RzWs|<$t_LVy>+-ze85PX`eo4WUW>d$^)WSMgqqHuguasN^P zN=(#E~q#^n;Gk8*&4{gUqbp$YF}8I)Ujzl9L7 z<4gLl*WFdO-Z){adSp4^bwMTFFfmXUWXu9g>8!=F$n5BWotEbY?$P;0EDw+l#S2Zb zHEvwf3^{mj$0*~(Q;;zmD2yLp!i@N3P8rI*?h9zUtLfzKn=d20_e?Zy^2rfdU$3mg zhaHdV$`3kMq~a6TSw7WWS5v(WSW)-#S^DeJk|f2$?^SdmP+}4h$7Ma|eYFPZpE(Nu zod6#8mFNDo-{X3E(VpjHFMb{V*l-KzGxnQ&1I}uPzsf^eWR4iLv`0n)_xWdMISCcN z$H&}FK|oYpinT4Rf^P)3H0=?K*I*|nr(ZIbdnkTA-n4QH?XKyL9jATO&?yw>KwnI3 zQ?A*A?9p|J?5~s-UvJTbKu`3D<@o%7lOkV4a%qu{tnx!7L=q0^xm^*c;72xlK60=2 zhGacN-GA!dFGC#9{ApzP8ptDz%{uq~P-Vu04GJFEExiK4QwqI1R&O*T%jpbOMu^a2 z*VUdE6YssLkRU-)+V6WgYr5`PL7_0DrqA$gr+?**6aHTxvEGEU3h0lRm}dX>WVKzQ zlo0XKL5AX__XpBZ(hNPpma^osy|EU1hNZ-By@#d@E)?O?o)74f;;a$)HO@Vtf?tC4 zX>w;3JRg=JjNJq~AsbdA*}Sv%a?{QR%F)VbV7IP4xvjul%qvHk7LW*ZhaBAaI@do! zo+a{npY@9EeL=H|a%Anfj$F$I%vq|OG&j7SYv#Od_!mnxPt7gBP!S%uPzJw0c%jJ)Y?oLIiopS4%G{vYWLZnlA@-aq_3 zmZyTF!U`H&u_SKh0I32L{J%z5mPojK{eZ9C%Z)M-Z;N`t39pdf`Fp73V;OK&zIl43 zv%5%=!kocu2AY$5XE~@P1Mys-z&gVp!RU`ul9|X?LQ$NcwbNTcU8f)Y?xmgsE-C&E zUw^%MnA^=CIOVT-E&G12Y~=PC)Oz zhbtQL$7?x*KO6CTsd(h%0`K&C#A=>_u$O-cB{@I>QHhe% zD|bAgi3B=}o!J?{UsJw=1GvRO-Ui5NI$TlRDn|&$85ZdKX(OPJUhm5NnhO(TDMwn1 z*na5Fkh_63C_pUu?)d~tFFT}gngpLNhYk8RaKg}h0ca(_b_IZr3A3sz7t))+^6^)~ ztw5#3w{`XqIH`d^SW=S6=r{nN1pSf-C&6Y1Kup{pvU>k-R!lE0C3*G%#s~=hk&}!9 z04}Ux%~%4J?8~K{BtWbm0D3j9>V7bo?oihkd4T@$x!u93P&INTq5@=g`y25=6e7jQ z31Bv?R#I0y5lz4&OX|tpkL8 zo$mdU28u)7^p{J0 zoYp`0iv}9zg<6DIc6kM*|FjR|f!yte ziomZzmpU)DE`|X+q*cYHmG##3HV1^r{2se!Uzq^5a>*&sC*W#u0rn37N!x0vS~cHE zF8t@-{a2FPI1`x{yvU6&IT(qM3Y(v_4?qS1MA~8w62vn0sd??)#2w;7qVt<{5t6GZ zpw;`p<9R}AKVOfW(3s`g@s!FA+7Em+?tB5Fab`=-YbSz}2@5<*f7zj2EsVw(*cvlp zC84ok?4Sq;_ndp=oKx$n4&Jc)%giQ^m~m1Rg2(adpXXOf(Y&#ho6?Ze`j?c-`zK?3 z>_D+Up-es*^ey;sSB$$ndGb&FWvOvLd~bNJ{!5AmcOU-1fO7qLk3nLK4zT|;Y^(sW zX2j52*S%ZKCtN(${&LR@?V4p371I`ON%^7MK)aT5fHpWj${X7;iYG(v-0$BB+_1k*p|9~&_ z(G}Bm^CydhyK-;lyc@Z#Ct=B%nLsWl-%R{3D+6RnB!P@(iAC~YAuNPLpFH4BFe{zQ z_cEZ_toNdi^Rn0_{V5%zs$$6Gfb14m8{ZDi*g$wusn)-7z zW1!QV-+s!gNcuNR`rop|5DccpqtdA1mHs6hHSW7Z(6OJaR_+o$VsOn}J~N^~9PErA zAdI4&Mf6t8m2UUlx*Q(X*ZT!jF1yFYXhE1A*ywj6)s%ubj%QF(vPqR740+d2_atYh0*u`Yu-qxJ5k(HEg;1^u&N-Ze25iU#Oo)Kr@P`K> zbMu+MP;SV=*vXSUanf{E^tVfQ>;AdJ9Lx^nwLVb6f7$5?$3Uvd7f^tkB`y~U4%FA? z)Tn`tZ_J<&WJ=t6 z+)E3YZgcT`tr#V{+2SX=(0j;0$n5mewP*BK~=t+;mJFCVKp*fYMD^V+W)zi?+ZrVX7F2taZkY!jE#;MM;`XD80B;lB#3Kkqn&3V6p4 z9No(k(*J|2h{(YiIb>#YWygp? zUoaFPJz_p5Xuu9d0JL68*8;ow_CNE4KR`A1Q%jIPU=qi;WC!(dghv{Hp{dPw;$(ps z+Wt$*Fq)jI9BO6!^HcF($fNdx_>rhfg%)+rDgh`{kSM^adIYa`y>okspcnj~FBbo9 z4-@VPNYE}?PfMz}BQFBH(xES{>27)BBAnxEnn+fX8F7d>fEF9zL7twI@FdtFp$%+a*Q}sdG-*_mrrIm8Bg+6-W z)U^%UY`}bcf6ns$ejWc$w$)7n(2(EWoS9=0h3YCIDqlH`8-ZL~5sMsA<$@i?bEA& zE8a81%qF!$butfLz`qMdrP4`n7}{A`d3jmWQ5Ru!eZXDib>B%$WM^c-34nF-#O#?o zAvejnPVvNq&^Zc(@6T>nib9Ubg=BCs*ga^wz2vM0=Sm;+t;OiB)TC(yx2U`TnRB21 z{+#q}8l~z9ceL|?&z|{gUA$_3#W}!(bN9J@|8^Cs0s$&`7IW8})7bZCN!)@%%(823 zpt!^+hlj-k=X2nJ*)w1^`%S}XQU453&u&U^{Ah4IX|@u` zt|v$OVd_7A0Z@!24sPTq0CrP;TS}@7&FUGG0bf2lSk*wOYT%6Yna^MP(sKS{H7Zhp zBc98LYhAlEbOr_reLfr?tPIL?i|(e(a2opYq^yIP$)#FAb2;~ynElTMsKY}e=Q-#* zC--UZ%c98ik z=2urbXCbv>M`4DSTvK=~mFqC(3jpl6c zvH+}dX|cyrFEzTAytFV{R@tdQ_kM4ZR^-Vu{tg3WOL+K;Dfl(w>mU<&@@!s0wUbll zt-v+&rDp@T-3R{*uW4!DNG{K%MD;C(Cp2!{2=BO40?;Z{-I+_CWcJv;Y?rc$jxGL? zV~WX?15BA_hhMZI)8Jl4*-00kNnrbmc~FIC#si=9sTN;}XaB<9F-BdTl1%E9Vhe=C zlg8M$m;vOcX42E@l4O@t%j|kp3Df{n7vI)9%iObDY#b)Z6pu(B%}c2Jz}_~n$UeW& z=#G)Sk7bt-rU{VMLH92I+wD>{z9y#j${VE9q8~n_asESLY>#Rkv4~g*amrUDm3Czg zxbOJ&XsE_*1D~WGugOFCg3_J19;(Syo1KhzwZ1;}9Ig;nD%1F-q#oy0p162tU(53< zsr_M%J(bK5Xw%twe|!SAG0LZr2(ru@&&F(}Thtx#HG_1v^^D}Adwu7ZJJ-$9B~1*@WBM){aea9jl`BhKC%xQXOLs;;$t#5<$o@c zu{rQrKnHP^I+ltK2rI2H<2O!cn79?l(!6iJq%SoU&XRaq?j<0WnC5r;9{Y)nRnvUM z-pCwu$5uz@8QS>7QF3!C=AtN)cmoT9jtOOIzFSXOXB8FdN5?&?SL>b9Pj8GW1FNl> zf+?=3if43oK*N`zwzkFUydjRmJvwH?-ThMgO~;fH?mrF*7cDIzl%G3_-Yg5 zkcN;ei3dH|QisiBNM_rjWzEMokMOqKlTCZ;zHov}FOxvF6CbW2lrK+s#>v>!YvBTC z9{W{#YYCvqu7NrAVriBNC50@vaLs?Nidm zGL(JT{F9n@+u}t&wG#Q&>D0g?f?5l^UUr^TpPh@ojq?`lDxCBru-kBxrWeW?IGz+K zjD+vd!a*F;k}N}Jn$9QGEn#U3Uye;z9Ms0PQc=we&GX^$Zlo=kG>?EGw%pZxc0PaJ z8Xnc?f5BCQOzb>Dpnd{f9}l`uwaTUWBJP1qu-|&&ILFyJ#pwsue0H$mdIZ`-#W(x$ zrD&3q9Zi+q@5gI`bR=3P>b#k*GK!WncRgheyGgm)>}rC#m209jd4_e;yk#AUnl$8+ zX49>FY{cZ8RS6^Ct>c|2UHB>w^d7F95^b`$$lq+@nQ~1?@b)fsnghM3IYgOi5rEe^ z^|MTgatk)OLav63_q>#Pi zL=G=9pmmebiRO^0(c3cEVb80^W_H;f&V|p(;qFO?4<1x=aj(Fx0?CI$nHvurR40$D z8b2$wH{rNvjbEi~k?XcJ)ScWaLsA7L=HM}*G9uBBZ&ObxKcwqJb*=LTqwzpqW@5e{ z@swI$J=^O8pHNRr@KulJA#(fbX{_k3;i^jhsGQ=B1k#03vy-SMl`z?iW8Dn%z6TN5 zr%yLesRvR|B&fT@Ff@~9`nE89CrYP%M70q77y}LFEfic#^Hc8i*6_p=PS-2ga`WtB z)}~1si);`SoiE$+Bmse@i`{9QwJQ=b($Rr>Tp6l5fx6q65wRadB=hyJ9cPO;^`<&RRiiL<2Vh! z=YG!Sm8N7f0aZD@XOQG40V+P>YKbi&H0lZS7np|lOXbbO>#Zeq#>?}r4QzkcQjU`7 zz?bn>UHfnL6dtrpZ4rHrR&8!9iMnK|C#2=^Ue>6v!8n-C$LrXHh;EHaH>U&v{; zP;r_a+_?XWcmH{&=G*4Go@%+R5>7(e%1cCVmwriaS8=AM$giXc&$mHSZshW*5KmkM zWlubD%qMrIT=wf*_WrYobZQ(DS0kf_Cr0I*>z=+3@tr2Q)ZO2kUBND*ms{Nfs{Fuq z%%*uiR*_uVr`6@EAxgMHHR3+)kTSLcPxb}&z3E=p1s!`vrL$9;qpnnR9Usu-;pfui(WT1O8T-e-n@0~u*(SUPvQ z1a{Oes{S?lxY9*(`HQ(R%&Ku4JDRbwYKCs33#@5jK4xp38KxQjG+SWUVW9RHx80XAcYB;4RGjJVa&X z2k2c3&uMFA`LWbGh$E4;hwe{%F!Am^`hI99hO$4{Uz2U0re+gcdNbJRGp=8j#*-o) z&pz-tH9s$a9oAs8{e27BDZXxOkGikZ82O%7ER7Ah%9A>BD;J&dB&wCTme-%Ed@}jP z()K%$w?`KOe|aW5&z^kUxW@vTD0_M9A`$tyGUg}|{wjNv0Zve-;hq!T;mPiAH^zAz z5~AXr%Qq0n0Xo-@JJiwev@Z(>r|+Kx4V4Tf9s>*)+ZY!9CjT6PT^(QuVXw1oqr!qi z@ELExyHO3kB$7Al$#om2mSPtScH{HWl7?AD)$zH=^65ncRtPl{>4FQ#Ob@94BJ?~D zsKSwrm2bou=5LYHh5UUJif{SlZBsnz9cTg3y747VwC$oEb>k|zVH!EJEb}IZ^FqNQ zY8F{K@#XJF{IKlk!{#K}k>nXUp53DV?E-jm^u7gtH?Q7&|3I!9txO=Yq!`)V{i1M_ zL|I$Nf_sN!aYT)dn?&D@yO3msL-VNu$HO$*RQ5Z>OcDIvGk;BqyJ@JbEz#Av{$yM! z*(-UyqJjddDep9d@AkorjGNI~H@@J@)OU2~YY+GJTY6=TJ!D)RL2Wd_Yol4(Z6{Gr z1V|HS7{b@C^q=L`QF4O&A6aY}_7yVjbU1nMFfVeMNaagA@HTIJN$st`AR|eRS2B7y zrzjrHSK*G(4ieJdA0O-)h8abht&@PMet~b#!tyRb&5X^uB|U~iJgILwwK2jMNI*9( z+oxgKut!-y8)9Jrh2B0js)X9v*k;7vtAb@Ijb&@;|H=x0tiNVa^W^1d8ATkUWZh_D zpB~zXvO2p;F4)sE;FpxR{Cwx?_H3lnsF|`GN=qPjHlf;=;NUSv5!9D`<2iY@Kn;6P z)7-8$JTV;Xb7K`U#$db0jfG3>b<)MDWaeA7XR-s;C}Bm9m)=)WM!mAV2`{lWfroRl zN4;abH+hV=2r3VZeRGNa$P%}G$XPk2uf1UeFB7a?2y~-q&TYs|^^>Z6k36X-+HKK% z@uY!Z>CT}~Rj4Iq;F4peGV=u*>Td{1&Lj<}^OcauAGOFOZ|Mfqo>e>#ubV5STHqb? zzWxes!>+3Qt}{s*99@HuSAvjNUhT>?8fYhi8iIp81vN~I9ZUFKcr|bC(oK>M_Li2t zfqNCh?^qYEE}N11{4BK})*RA1pY*M7%=o1027WMyuPH)^;#8G#*@xofh0J5Lc4c?d z3`}Nu>KQ|bgkeWWq)YE(|2sxf#~R|XRl&$0kL?m2f|#`&Sx4eX$GTZXR0i9^pVxzW z&kz1)c%6t~N4s|D($&JMgGCj%g;Qosb;AOC*zMc+F@@dn9y>bV6`N#DX1lj~_iusA z3fBmbLQl&sHC39)9l~3GeYz;t!JqOnKR>U^gi3`#k9gNF$xMr;7i4zV^S6|T%l5r} zhB|#1_3Vk3RJ+6J7%P|fl~fZZ zK)N`@eEXUBD^o5BN2=QR0V~B56b^JhKnHnpd+5J*B=eU8rzMOr;{! z^7FLn+aIGS(K(YdqV(}IG(Ne?nK`u5t)UR50l0uumoynS(|O*6HUXFIdMChSNSy6Z zE!Kc5@uf&<=47Az5-CkJ(DNd|#tgSNw5)$3ys?>mr=U_?#p^4q&+MLAGcg#E_RUjj zEDal_RV_M4aLc^_!uuGl5TI^Xj z&Au0HCSuhXakrKrrv3q*kb#x_&>3>yRE6NMM`LFFyka~8^oq&q@H-{PCP|5~YHaa2PFWA#SQgk*O~-W{F|i6sYWW$%ujsZ8xAnc9kJ zLjnv-3kte9_Y%3v<%ErauvZK*_JA}Q-T48qBun;w2H+E_AuZ*v0$tbYn3P(?P4(*E zsdHMzqYPw)f1@&t=Lc+J6%(*@!;S$_`YKD5eFE*jHDNb8fYfE6i@*vX4u5`(Iv_h#*ln_0Lqdk2$=> z!ycDsp=##n+fjWMSC>DdgoABcihX2@-!-QT_#$2c3QW#eSnZDQ{go78^U3BS{(#Z` zEAg&L+hMjobMuF#!40Kq|I#xI%iu<*Y4IX~$Oo1Vh^NmZ+P78^sY`bXS+bWCi#`J) zO^qDvJMR5~kE~RY{%9WX4Se8ChRz&o(^%DS;9Ph=HMGmf>m$R@b;L9iy>LGPrvX-^ zK%bE40XM)4UG8bT(e!4ynMA!5fo|2-`~aAOf%)Zxsep8nXHpp1w%8P&hwD=xrEeE? zlgcBJgIUhsmb^02M!N*#{v}qtcSxdZy0RlhA%Grg<)VBTZ1ea@gyn zIhZ0vkpHajGs+ik>v5H-`_kRbu< z*D`Kf_m-Lzuv<*fuMq;v*J}*qZN$D&Kvz=&zoox)@?&YxKUfCqZSUdJNTja%Hnqs> zybGf=FMRyY*lXC*Cj}Aem$USHy09ZJJ9%0n?~m|BK3+Y7nyKpAaD7Uh?7h%6dp#Fi zGRYO0cs>Eqf?4xMk9(f#jnpK6W_Tyrm)W2h$hz;BkUd{&H87-7-~%wm_rASfoceq_ zwE}VzV&S1qES>IhkbuVx1w=$FPKw}4=s|uk+gHswpQP1HgDjfo6rC#%d_-%wpuOY1 z5Gd?RCVev5cle*>d9Yq$?9WX4V@3w{3LS1EyTp&m(GrM!Q%GL~oO7e>%MC~*oz~#< zhV?NeX6}{tJ}nP}zQ?oG-gdTS=vEdsTVe}U2y7wM+SM)3SCfd}KD%MD)ZX^7cSKp! z{si=rCWI}a3`8i3e{iSgsj4Jz(JV$ zTko;zsNCG#q*aG?TF0ax79F`dPEEQtzHn7u{&|2msroaXxrsdCjnPnTk-3G(bJ#L`m&^-b+y_c66h$5gxN1k z(#(zP`S$=pnMaMye{*+BrtTiL?iwCn$(*=)eU7hy{1qF?O%B7jkV zvpamYGOwDS(|4NR0Z!S^+bu?YEV$;A&nNzBMvwxM)kR?pD6|XQY4^@?BrBfVQ4Lih zp!|wS=5J<@2O0>V@yat;&!N-H&ml{0Zj@wB3eMfze%sbD%rUV9ef#)l>r4}!Yr%LA ze-{OcALp1PJ|BL_>E@F1Ukwy*D;Xdn7onnkTKt4_9pY zvn&Z}{Qdy7E5#R>itK~I2cJ(o@z0RkudInu0Po~a$y-3VBlvvzFJy&9a1AZJ2kb%d z|5#E#Y6dnafr+{_h5t2Ed=h1ETV}sbK+wp+!NG&CMd#9`Y{5hgOHa?}Ni?}K>H>c12F}=M#I* zTWS<_yN}>zx25M`n7$sFkAWc~x1iulPfw5OSP=$;9|=Ie7Y~?kL|P1vjOlqdz(tgE z%F92+#>P%9QNwYe>&23nm(`W#gCUL+^D5JZcv*E?eSQ6O`CBU2+I6I9^Z#H(d#h|) z+zygF$Kok*nmu?d>w)^x!c7MAjBd9anHA4mstlQW&}=#NHPW{qkbfdy=e0~?qdAxzp`8ZvinYE zMMg#8qym@1n9~BfT_~!L7u7yl7ai2*! zCA+y_v$L|i>XZ~rfl5Qw!OqT8YPO?Ox$H=KW#@ssSaL*V*0np-{Z83%g5)S9*baX$)(JX7tsn!VT~-<>nUR3XJZq^0YuysQU~`Am)4hsys+vv zeCwnO7Fj28&`?uZNePws$vnb2oocJ5Zz1*7UUQjRS^}jYOFqHKF78(y9J1qtPH4Pi zG%@Ysil2sKu*`Q;Ewpj3o^`oug%MNEm6i4w9UYwlSd-V-QTPej=gj6QggJH#?5S|C z$CDiF7cmDh-Da9ytaG|kizVV29VVb*eLf;#?2b9>Xj>sm6|evvp0S93bK?L7gKp?` z&DWQn?{*;vehdu_HwH$Wx&|Xl^UH5;U5c{CE%RP-8qV@WT2HNIG$s-T)0}!L>H`L4 z*lgyI5bY(q633cdUa#gl@Cv>-MGMjlnJ^GZy7^1xn6@gqLl$wHoI28^sGu1qS+HrQtJbo=b0RU(chaz7Z{TnMC5G zjW>RDgx$zybrg=s6%-ayXiFmGo(MJxLX%)GUSg0FnLX%;NZHp}GX+Tf(rfC}Q7kUX zNiC-!^UJ=#W7Ppm6k=D4RX1&(XjaUgN1m7BYm9wqg{WkOn_y`^Ba?@{+3PlmBi9Ac z_cL(M0((A}YTQ|6T`!^N`=-64{al3Y!_|3-PviQg{o2kC_WH6JHuN4S*8hD6kUl%o zr%ce!$#L`|bN~r?E?F>9`k;G%N4-fYjtS=F3~TlUliDpbNe}dI@fsp~l?qN(tC0h4 zN3iVj;p|&4)LCuEoY{$iv-;#&V;2FohHv{Y8Y4VE0 z0r*({l1)E6-(7u$%YQ#ScSmj#KUC-!z&szP>V(w4aRc>iLLE#ge~`uk2mr;hM$LrQ z=Vq4MvPD^sFv}ArvTY0wgCG$#ZdaQJv>N1^+32?`(q{J$x@;h^2BwMKCgi6`*F>FS zMj+i#p_fs}|B+=mwqEFSyoP(8z8{J0xC|ixpaYk*Z-rG=4`{4)iDfs(Lnqt4lCpXT zG~{5ZzzKUw;SSuiIxlU+B-!z^<|7Pmda61x!N144F@`KwBmgy}l_(QR_=U30RHOLzV&~_urcE?*BeOMNT1FFA zW2(H`1!95E#d-ChJVmBl$(7sN;z#gW5hixsJ}xoMw61M|o)#`*e!Oo4yEFm@UYV?T zn=pg_52PABtHn@#+0DK)H=pO$;m5SXp=Gu)?d{M)410Mf?*`_BSy$QB&~4#>(Wob z?t&m&45DeRrg|Ie#DasB>J&@PlE(G0O{&prv#;mX^*s$bVv*rF<7I;AL ze3G(r7g#8GCT6+E#Ne9G2ecEMzh&a}8x_BXU{A%>1!;B)=E3frE?q;bKe}MldvM_n z(*d6ZfQbjH{ME%FyTmv3{cK~PYa(t39-)O{&QqQseJEaal<<(#rNS_J*X%agoa{R@ zSc32{?O1|DNBhy{qb^Pk?x#k~@eIE>RTvUi`QnAV-;i!#fel;Zn^-l_ebE*tySLI` z+Xe>EYxkR8HR>niRiSu!*W~~u`y=eKocGghM*))_dmXvtP9#FNPKQ8o{IZ)>^ z$!5ua{Q9+Vzi0=H^$CJsD*mQMhTUc_wxVLOf4Q7-ALbm=_ycmvT9&UsnBvg zva`fch$nkYJwP=;HH{*4*tOq5J&%tR6%~c9&CJfvyI(388R^Ab+9;HxU*-*DH`HLP zqag&BZ%6S`R-HW)@$1BlVV1&HIS&8iVLn+Z1Y)wayS_Dtg|-?!ejvaJJGxz&PpSA1 z4ywQXYWy+pN+v5p7j-A&ega>%{X-)a^K0X$Q^h*KqO*G)$Pr5);ae*l=o)?jUD>5z-aexmu?wN4PN~yMU_*ez z$G?r&5ZBYw%M1kzmL_9dSQTqx+`C*#WH!Nb(6+ODag?dmLiX|MHkBf)AW}OdRQ*5F zCS~$_P$&%nl7UZcU)_`IlbS4w5WzPZ#nj(yysDR@sMMA{DB~_gnmDdW1B(G8pnOJH;Jr(LG~x_GbG1}p``)LfD_!e#~0B!~cNCby%X zQjG%J$j>zBu35*eQgam3GD5(Mj&-1$DeDzzv7nW=BD0^n1oU;l3XsS~V1O|48O|!< zNHI-m*O!}yS)I|*b-44^cVP|&1gTWsbO(!os9O3KX&7jIs#nd3)xktl4I z2(S+foZgEjMGJI+Df!rO7ngR;OV9&~^iNe+8FTR>u8HVIkilG;D%uy&(m_`}qkG3t zs^Xf9WHeOUh><%}Ako1&WP(#m5>dd-tVpE>K(Q%YtpDWt8PbK4tmb`>}jghuVCy7M_CR)os<)@&G2P-G`#E zXF8Z)U)DeCxuK^TuRE?Mue|vVNZnK7&}q7pHUMOlo%);gN!Pk=B$?+zDEcVPU_Svd zje*Ju8-PfQ59#n;UbvvfA96pxr)DBH%xiLCg9}3!-E1$I z)lNeKmpfq1%}Gi5CY)#HNxHhbtQY5vPs1=#C4Qxyb3Iy952{dnjVw~VKreFq^?}Vf zH}e8b{R0f?ETXz~dAX0XNrbq%MXJ>(9v0@@+S_|;6yu~ZGSxaem|ii3?VG+ti5&>n zJ)x658=i?Q&&=$`Ygu%)6wVozK}f2Bl`XslR3`8%a0X%6stLoxg43_ZeRdm1L}AC& zqf`ToZSbj=3{3qy)RCz8``=J-@$+_0)bO~{D`It*qUF%Iu}b@j!Hm&x@oaZOMNN%X z=?N~EVQ%EGtC%U#= z9P9+56elLI3A>^a;Zh%@T944+|8ZItKMVbS`PbI`@@DkHpL}>z*8NpQ_WZJdGVmz| zm866a7XHxjjY(q!x%M^RzQ1}o!*+bvcQx(zSbcY5zTD8MCpK|rL(=a)%w-hk(G!TG z8Lc0F!}A;e+GcaBc;27nV>R9VffkWKmG>L)Q5F1RUeix~npOo} z8*Z=C`FGCWh@=8wLhfW&nE87G$9K4Ca%twL6>sqKqi(9{t>y87)E%&m2_!+$Temq& z=kh=mN|LLqUD5u;Z1pk!UETP-H*b{}Ebu#oI;M`7Y59AKbvRv5E{$9fwRQ*J@sv$> zM>YNM9e=*Z0&mht;p$w+B>w8p{(w=Dz)wOF%?u&cb4p4jJ>i&hUV}C8u?ZlxJ)1T* zL3F?QB+^^{reS<=BW(F>S40%-H*qrP_%{185cMwGsM ziD;(^K^uC1ibs!&UKw?ZkKgR&l@~opc>u}@x9T0$!8R`2EydS>TEI_9$ESv3+`KEJ zKKmqu(y6a&idiZ$&F<3NFDdEc^bk!QEaY`mW$Rt@2yP9pzyA_c_Wh-|Z!95sE`1z! zi2JKQzIbjYritjhB@=Jzs8WgANXm*e>B;T44{P_8M&wl99u^eJo%b(^eC4Na;52Hg ze{K6Y&Oh(sJ)^f&_qUeEG&FnUrMW9{Mv|Ak?Q{e6K7*oJnGhBSBO_-aq@)1QB|8w(L=q}+HzTIsgc zNootj*Iv}T;)aV5X{Doo`x> zBsF^(TnP4jkg9MeMU|k!oxkm#>C$dyJG>e?S_e!cxP3Szc8L1tVWsK?R@VBgO!wbv zZkvJ_Y9i4Su7=;czcPs5|6U~9J0<@kEe51>u)b=aKBN9tkf8W zF45R_s?){8c42KCYWAE!^dF2NceK@`a!R+==lBoq@OuRuMg{8fQfpmK)YnL}qJgB% zamh>^?Y%a-AA`H;xvOKNk`Mo;_(Czi2tx-_$rzL?;&FN9NjAj2013%X>d!(;1bd^OpDU3nQ}Xrhk-J12x1dwzn4q;-;61@w{GY8%ayb&a(!^D(q+YvQtGI%izlM z{{6O&H&gH|w8y6r+3qplVvBk+-?AUy^6;!h<(oOU+{g*U^$>=6qnQ)(z^_%I)U@b} zyMdPXA4B6=8dkLyF3m#4F1A)hP34?bH^z30I8OgZZED%Lss69+BT5c-4y7Hq4J3PB zNSrZr>5qZ&tbLcC=4iMvz&ArDMoEoURLLeCYxFiuL+h=Fs2fx5+l!pgJM1PWr2j^^ z#F8QuUflVLb0yTtu_UU)%fSg9pPyHHKU+tpnIm9+e}eAq&pDXggGqlsr{q%PzBd)= z+G+r-7t`)U28D`AvrDM#EJW$t5t|hR!!0v&vNenr=fs(h)N1s zfquE87SN4fNWvIG=qr}a4F`|l4|4{sE+rkEU71z>(ue4@W2(9IVyy_fUiEe%HXkA$GH?owe}u~L}^SXc871* z@6CFf87Atti>RAw=HMo)P|x{gYePJkbADRTN1i2BAKSVLCE3nwGI4;!ptWUq8VEzP z%rkI~+t$Lcg8{60$+-O7eW;Ui8K$<4Wei~_-sTGpgyw11`8-7F{n>j{Pul5|&*_D^ z^H&~vso`pH9beo@A#95?`-e?n9+_sX#@6T5C{DEv195cyohs98m(QoO!>@1xIl0eC z_BP@(xT(`3Ytu&DJE>Z=!`pKDPGC=oR`|N-|MooJxk}v>n=S~^To}F?=kr_ZgV)N7 zuoj7mf;>xd6WM%UPiwX`Qfz`>{1AGLki(|2H2N5scuZOkAJI^H6lE?}YKSPcw3>HM z?Of_SEg zr=G)>zC0_Had1Lf!F{U&-lcV7HzKVfn?LF0zSO$a{3_9>b-FaiT{*6p{;Qvw*K*>L zok!0?m0HWyJdGDFyrX(}Uy$E`N|<3xjW&;V2t2Z)I~N_D@9H$k)Y6}p({=dKEuM#= zE}thR>K-JD`__3x#-e=UO3VL0_TD|7>Hhy8?|pSuR|mN|NI6z2MMyamp(9;NDmlkU zk;5Ef6EmxXN=OHa5Xxy|jvI3dWtHT(4YMuDX|p+QW3%}^r@G$P^?qOP&!4})zWYzt zZM$Bt=izazRsi%pT$6{l6b z4>(ieN~>LE2oJ8~zOyNszH6N$_@%{cSFaIWWV|1Hux$hKy(xX_(iZm+SUdH~Gx{iG zwUN)y|M$(>-^m{A0;NKoOBSwnwxf)!cFM8+%K(_)+f?(~A<^hV9ZA`_vf6Z!c5-UrZZ^s{Y4x<#8D$piEDqxB zX!w=)`{Ya8sCspK#!PA->pZ$1EAuJl@Y#@e?Q>~@hC>NRNXU+vfV!)WFD8Z)NyF4T zD|AH&J@*_uZY^JVP}!S5Kl*hNiY->8AJ#o*c-W)tvv!0EabNGV&xW#f^jyt2$I(z4 z)|bjmlO30e-)r?S!vgx};@{d{yG=M>WXq0p#nSdWwNRBsvzo!AL5A@}t}vWFzZ1VV zhl>81g9e%24or%_wEo^-dkH=Cz#8oP$h&Sx%vU+}XWF0AwoD}#XvXjDE!L{&G0$RS zJ1jQPpe3uaHe5y$eAMsb{L#T@Va=cV@|L^2GSjnXg@)a&(Ra&`2^v|wtHV(PC#bPf z5p|YdZGXN_zmec9@-HgjX%#7KBC|0jdBTu^jQ0kYDnjC9E=?rDgs{?u5oPTA@DPXi z6GnhVR(5p5IHI~8EOIf+Lkg!MmXO(U?ef?is zxT+;JqL7BPkVb@04RPANZa?9sYDe?Ja|7_>3JolDX zU(@0zx-s)izR}q-ow$80emi(_DY_zpr}A+I=`bZj~lv^RsaVg+|x& zF02Ezh`arA#blwenE9@lJ(p(0y;}Qpc|^5#fz3WK=RF%08tfw_PwzvN+o7}{98lUW^NEfygN~_f#bDK9F%`}Q8n@M)hns5)t6JH zb=bh*#vk5X^Jpr9PJk(Vm8*EYTBT(SvFoIgDnIOoO37zFE`(2^;ob&yjLcj_P_sr& zCX^U$%dbZ_!Byk^yj{y&^M}DO&`;8II}dI#jJ_8yH7PX>nrf96inmNQ6}_LFX4MB2 zOYi-8V3%waz16?c`t%9G-M8o-T74f;9}XPvGVEuJdyy|1Hrsjo=`}ljS65fs*F4@6 zmKCge>%`suQ~*# zuNpf_7&&U6jrE-|mp_!sTQ3Am+ zyYzB=D2O}&%L+Z?ciC4j%b;svEp8<>(^fA|H@4Q}c+$oyLq90rFSvBi#?{Zn6S~dQ ziT8A_kK^|`Ns;~F`Dv3Wj}w}n{PNPPsWQZEo~?N+rT?Mp<>6sbpR=D~lc~Jl zDtcPV2G55JRF~!{`Dd%MgfiE@&TYFY8^)b&DKl$wr^cnuIbWsTsc(1xI^p9tUPVQr zYL(JQ-1_xdx}knf@K^uh=$jga56t96(x)b4O;IM9Dho}wKbwS6;bm~V#?e%L14A8! z4ZcB5Mzw_evNV;`nOUNyU-~1U<$|7-SbXX5L<_OWNHs_mGz*<6`xmO&_|LW53I%7* zT3#Q@ZYv_S64nRKUw1H_o%)F>H{O%!$zUFTE9yzDtN3IdUKt)wlWP<$0*!h zbKKQl+`lS#JHwjYO2_-z=m5D52s1%A5`HFLtTm9|EoRXoxj_-0yH08V|nfIZ@`*}RN|H7&==aX15OW$XF%Mh&&W#V^2 zE{?;jR7}2dUQEg+$~GOVC`M~HYY+=b@CO~O?IPaWV<(d}OdC z=Uzu>VAXP|s!E94aWFgdwYFZWpRPV?-!6uuM{s6>Uowlrb^toW+r9X46Bk3!>KSt?> zjwALZn2xukH=3_s$=cP|$VE8t*W$F>)B2v-j>pf8DJc#&B)B3mc91sd>*mpE&ly?P z5G{k=v4^gD7ix^T2U}b2JuqboQn`tIo1lwr5aTN<#IxmBQ{wDkLGO&11x=$x(y=LQ zqh)7g*3#r@Lu-v{GzE{h@dx9#kKx{NgX%m~7;W!Mv_n4Y)3Ai*pB~}}?4FE6Du}KhXBnqIZa(RXpSnEQD<2+4XIt7N*k`gB`JS zN7>$M7zo<59GCHlTKnD_Lz5}0Lhj8}_cuUYGSE5Gn0ES}jzP?Dw^euqzg^YmTeoX* zLhJ^wpr{thSM6I;$)kZ139|1Wq-O0l7Q`t8#Bar(C(Yuh@!?@fS2ZdQSxoC%OwW5t z7#Dc1{M*T$HS5VzyqyK4Df<1HyrAN5MaDK5t%JsG@9(qj&DCG5gUF9Q&R0IzYlDsb zb)Or{XD^O45Z$dJ9d1(MOp+eQe~9*tBPU<<(>*(HT*gsU9Mux`Q@$S>SNDGpCk0b9F0De`F=hv6*v`y0|EjkCSip&BZd3t4frYsra2dYg`(BMk z-}mF~imB~#lVL}H)F66*VA1o%Rj68=lM6w$^{JHW^il%*D5AM+lLz%qKcBjkbF4o> zP@lD-;5|E?uaE{J(vTUG%%dJ8KJ;y`W4v`YPuLlBX*+R;rVo2Xt6zw?y%8Yd-#;U( zU6Euuv)>#LuFV&AWk25aNV&{Fj+oZ!QQEz35HhbsuZ`YL1g{fi?Tu(eJ3qKAHVk16TdyI!MUbdWWi4}zohP2dW; z+j=*yaVGH--IGycs97A_Ur42ig+!z4gTwE~!cx8@XIK@yXB6Upu{Gkw;i*`G%M$&G zS@OyFg$c@7&5|>JroPQ*nl`Vz3!bAHJYZ=^>2j23HlzrG|IHzU^9Yo`ybf zw7ebr`PZ~Yo$sVk$>Qv{3Oml5bHx(wr;F$AV4d7-V=p;n*fY?@@^zOy@V1~<1)8|~ zm3-|W9ZV>9BBcf`IYF(nvlk6zQiB3}Uz|$sa>R#5^AF#^^L0BQ4<3*O2dZ+8V}Wdf z*J#2uz;dRq+I6zw0q0ng(P%+aB}{qaFL7}da#he@L?7COQ-}w_)TPp)5Zbl+_JqiB zF3-Bz%q%uGcD?*ajzCr)-h7AGY$O&jZ>YZ-P2A$soSDUfch?nxK+s@D6Xov4GsS0W zgFxCd+^>TnP7#f?ahpbgQ(G|Z7Sath*Ed;juBuWht9k~1E+b>6(g>T=>xpG5^d#B% zRWdK5UJcoxo!1%B4K@V`H{wKModR{r~536RP};gy5h!yf2XC@EY%V7s^ML!(M;r*eg9PG5BgwopOd za}CKc&9n#B=^J&LbR@8)dIh8uV=l0TYP|FgPJ=x zV`nCxT6{jX&ad@>oJWY@+|fR_Zd%|oQ`H~06?7;7up&Q1!hou~N-(!|p;vgK(O*c< zB6Md*GYa=8=xMbB{ANClEWM!8TPMZqQDW*8dDv`Xp^f&1SZU5AuD!39en+ z9~v_5h~O#p${a-awRRgDFsQhv&2@3Un%bu z^^5c_k143%<>Z@O@R~55SISa@pShgTeaZyZ(?H40%4%AnZ^&!*8#NLbQAgmg?ir4z+|FuHB-aQkr)Pv8(ZAq@X*s+R+F%}ntI^-xaUzJ@ zC}h{jyi16>WOuwLeMtXAGo=SnxRPxl9NnN7$nJVmJRR{-KR)t~o>}WVNJu4Glr%dR z5PuSx(K1#v&cJ(A6DjNv)%fuiqUOHK*iIU5wjs??65p*UeQp>L>oVSM@>Nll(#KVL z3?j1Xnsz8NvpvsL^sU>Ci$r*J97`EZjL|bNdaWM)4i`9LK5pz6vZgA0ZRZGRpZorK ze`Zn&(3oCJNJ$<&>Xf(HFmjjmvf)uZLIHW*r6%l&M#4(IzbMTn8l>A{6I9&TiC6e7 zy>3sMR1}4`>%((4R8(9Ky3*qU!5;tpVomGXn$CryjPUsLkJuoN@=j*a=%$PLdf zH0kkL_E_n@^Fskbum4<7iSpOj*iB=TIP~!w9kO*;6J*1#@mOD557Cq(Py6?qsX?y> z#F4-wZW?h)bC|>#4gAt;*^OW|8_etyiP;G1FD*l75p(+7*~;|{H;%8kZdqtsS_jvU zYSpe(WL`7ZtqtzaILlqKN=cFXKQlTAkU+9=dv!{dq^Yn$U0D1j7`{+ai zA-F7>CAhuE>l=*XR^3AP`pP+inu=pL7j&s&HH|O7E_SX|{>^$xr6dsNgP4D~Wyqgq zWV=3@F*n`FT`;UdB322?d=;9^|!wy8*3W`n0)$3zs*IG`*l0n zm3tUIk5yw!F%1`kE&ojOmi!v2d)2y%zp=Z?!jUGE$ki2lH7B6;84TAgv|SZ(vyaMF zv$hXxw5X)U#t9A2EN^8V(O{hxQWs#nLZKF+s-v3y?1NAiTc zPrJuATZ9@KZ0GQ|t5E)gpkKCv5{bwhCT$Cdch>($8Dn~QLoK}Xc5{&Js{UQ;$*-eh z3Q5#F89(X)5QeSq6%7Jo6oyOPo=UAr4of*aA1(~hDHFZ{A%CFi(LbvfH-pvzoE77X zkN%EG{ktr&WX(lai=LunebhY>C=kGQTdZ~jH9eh0_3j^+W$AeUA4rNMg#tXFWU7fI zWRZXWH!B=ot-a{)^(e_V@M@CmP9pmEfq{;v-VzV-i}cX3H? z{=fB=ti%7$=V3dNRxz2%UxEjsy!x3|i#zfBRsL8-s$nhB8cM1J?R~$=&Zh>1|QKV^Kh>Rrztd&5JAGU#6Gn5Yny+zfSBdHb8xw z9=Wt+B)5FD`so-dr*ZrN^T0H_DV82Gu4WYGF}zAla!uEI@xqr~kd!$MY*|P2591Ou zGuS||(15cYP_?0JB|kj_A+FZ9fUI^qvgP9S}^3%A>(^=OGV;Bq9$OHE+_iCuOY2-#VUMP~|gv9|j*Zk3a*Z ziyJ1`@{%cYN1$CZINsyOm9j0!Cy*Um6%50t%AE52{Og^KqLON;FR+*rKljggD-ZKb z@3R^WgvFOG+JkWn^?#yBowvh)Uh9zu4TRxUX7Jh6^DqH5T$yZCHO^ftyc7}!xT}EM zmv})5$nYlAF1BTZ)B?~`w{E!uTgm$62*kjDqKHkm`XbU4e`d1ek0CZBXNNdsV|SFK ziV}F!pPmQ8$GTU7KGN8;Y()wdf{%(quQd7mK{H*`rhwL!!FdAQ0(fckdH<9YM;}&P z4gL-+rDnL?v_oT$;kZTJkYe;M{i(vl^SgsT+%E?zng)cj9znGtt~NfExse7$oCa^;v=n4dNu8A+TJ+v69w*>uEy0Y4Z(CrIizmrAcQNH~z!#@R8y+Q|A~Mg?(nGP#l`P|SA)sZfWUbw?*m%BVYbjutK3vm40Dr#3 z=JxBu`($y!ei=@+VeiCK6qH@RIHJ8$-!pm0^Lg#V5qNflgiF0EA2$cj9jyv)+*LJR zPaOMj6cxy=tE*-tYBpE0VZlRVmFwks9jqSF+}SQb=X zRpUqfjMQ`Ed>ygOJNex|F4qJ5|LCZl{sqZfcx<#mvQnjK%aDj;+i3a>rK?(Xf<&j9 z-p#3U{jI0VC=Tb(d*)cC$BMgzwJ#37vwn6cP_}z@qjS~O^w5~Ngg9$Ir*nsDv6t*a z>bZAhx|&jfUT|SY-6_aFESYLtmreIn=ZZ_C!nAWsC!SL1a0_zKTGlQ?>gZN$3r8mz zL2E>MAOtx$tUlJWt#PcaU!{EycyK&_w*)@HoACV&SF>!aA!X9UaHxIE^}e7QxZXD? z{d`5uD{+!2-<-$1cHG2OwQDcOX#Yg>A9RD4iK6;finN|`2X0;gzcEs zE-(K$xnR>30UD2DJLN6)7odqvYI3q4FqYH4;SsHTjlVGsjN!8P#n1N-ur zLijEp5u42p_SwO>rTN1U>VLmo4|~9=JGI=^3H#v~-2EBR#tSKL2(PVgNEu^%$rt0SzQJ zPEKxC*`Kd>>KrRf&Z{L5PIJuPuL@_P!`GSz86s%~mQ*yG%<}`SHcnEZpzEc$_KYo_ z^7KK3?Ex}0hzfe<@Z-Io&6JgWHq@mtv$+?=g+>`I5GeXsbEauQT&by|%`u)7Re5VOyF9p$VT5A--ga% z^SvYZ>`tCHVF5W`dzU=d(q-=}AGC&aAgQRf1b13wQ%CG0xn*6A5WeDSIaK#?of9_y zKo?|3W4>FcD?e@`&;(twLj}{$N*daEeA|b))!Vbq?E-=mRY5(vLePTMBIyycMhCJJ zh~o8CsfC4yXP;;2g?v1t4{15Bd&>+plLuuKH)||RWTD_K7U$hyZIGynrmV#D`Ia@5 z3E=2PhE8Wv_e`=nDXPnjyob%rY4ehn#dOi!<6V~cb=c}H-evG$;q3B&qKtxFGu4=P zdf$|_Dv_kSZ;W2AgTd+${L%3%H&nt~dbu3CJZ-cPF-OAt%-{KgbH9V|6&EM08jlZ{ zwifi%h4O$g<@L4o7ZcA5^O`0DTjPI|V!`p%D3pNNsXZBw&NcyULmWMC3bahLVl8#& zc;(D|47NL0nphb%1A3YIjDvaI+2)cH6DX~348-EfZ(~ElKLK^wNdNLPfI~;EMAWUO zrR_EL2IF8#8qMOWhGEB#XIVV2d;{mGNlYP#WGL`)qc4ih@bM74MQO*H$mmS|Gl%bH z$;IV|`%(c`@y(HhkKzKsje)vn-$GGFgM2L#o)p?OV1?B>*g8&xk^H(&-YC~lVa&f- zwMyE~&CRN8LBF@aEJ!}~3tG4yB}z^^#oz*+hP!O6pt0WB$J)BQX5W}@GozfMC`&Ud z;y?T7d@b1M+ZUv;EL9vH&2H|`%;LQn7k?v%0Kb@`J?$tjXlO2ED3NXB#v@iJ%PTJB zUW|~fAlFtKPdQOg6a(?0c`2d)Cy(MIT7<+Yprmql=7&AsPi-mU95loYJ62sPuD3qj zXb%ELoZXRd4BN~I{TvZuv%prdYk zadNEJ83A9#%U3N7Z8|#hJP=x+d6k2h%#rj9i~4%tQOgWqj!q$VcZmm{WSMurgm=Dj0^)koWmLhT+e{t9HEPbmy{H(y+;es2d}D5EIgt&rGvO+7l5~ za!+kqt~*)u3|m^Y*-9~q>0!NbN=EBa<$LrZ-Q?v82?J;m+4x>vx_CeVTQ#_0C9BCV zp8OhZgvZej+k`rg%(bTowqw*mzWe=5Ii9z_8ht;G3Oc0VK*q2o8Q*23bO^2A>oYDC zt%$b^JRKv6sHQi$Q<;4&IF}FRDUZuPX+rY&E<2BY^RaHZxKyVT=`JK zZEWEt1)zmg4dg`hFL0Q;EE+V-Yj#xajX;i(+Z9TBC>3>3lwBzINFp!cow$75Qp$-I zpZq)(nv?uw@~iv{}o&EVtIa#%}%cD=axiYJtb zu_2C`f!oVI7oq=P1&sL46$w`>#T3nDimXbVsS8l@cD$&5fGYnRizkKrz5f{Lq+uYg z%P0rvnw5}PdR6`f;pE)}fP@zpQ2E`(8**yg)`v)qsr+$ME3`OQrwWt^Eqz+I@jvmTCNIwypr#;ga4i9kYB+bQ+ar`rP`i zwd|_3-;WW4!d)heVx}@PTbZxbPUP1X`sS>2!CuO{%V~JXOUp;jh9r*BUb< zthq_l#aB3S1CW0QC4qGov z;(9uSL6n>%nrfqvS6V+0%WYTM?Y@+jyS*jWz^e9T9=8kj`xc?3ikDV8=SmxJOq<{d zEhRywe4Y<4KR~uR+28JXYmR4f&T^Kd6&}IgWIB9e_hBaF=%L#4TgV${QV3p}#`-}O zRUiH5uhoKXV;J($6`U1I5*kq^)^q?p{Em|AeCw8$t?YQ5y^um;!x)pXHtZZb<^+f4{pfPKM6K-cj7!(mBb&9A%rtg+Hdr_O)(}J=I5> za^=e&-sCzhl3spT$d_fOp76NlHS*ec_ZFoSvb`o*x67^fD0<4><=ndm9GrWPTAXlkaw=W(~{(?}P!#{>a9v!C77u z$S2lr1W~z-jkBk&EGyLLN{ZS)I{k;h>4&B&$Ib06lTKd49qP(j&9j3Y?a%9Ur?ONS zfudtHvmg5+BlFaEhB^s3bDtJ12!~iEbI90mmtdqL*QuO73lnyh)vS{8ws&f?3xYUt zmAEyG)z%Ozqc1MX=0;zOoO2naaSR)-hDg{6Ux9ez;ODDks!cRvp5AyI2TQR4>L3^a_K2B)cg*BgEpJDp4@U48c z!S}lrJ}@F&PbS2LZ_@*;IG`BD9@ygRzfA}pMA^{FoB5W3Cnu5RwtL$B$n}5;%g`Ojy{=q(|}Gdi#ZI z`d%0Y(OP;UK7LIJ#jI9-H3d!yj+L%3_jjPZZZ1@QUV5FafgCBKXVvl%8NK0LJxU>U zgj|YR*oNSZYY~vES&?2Tx2gF9FV9DVjc>JFD@c*Iu(LODx&g#LjW-%LO$syFq}`47 z(@7P^*l>CYEL!x{R{F<9otPz>;%!lm!b{<`nT;jJ7AlyOiYL*q@?-xt!2gkAW=KJR zhdr_TT7J8W?jwvACS$l$#{|RD|7BsK&5GBI2p_(k@3mg7LhbueD1X4fT+9jpNn+vi zyr@EQ4*GZWJt_&Fe@|gAN_d-9HB3SIOSJ;+DZ*$b+jhj7`pSI>bI2Zd0A=ZuQdL!h zW0g)qdMw34UmGO@oh9p@k+bvkiOQSTrF}pj!w$$E4I6bhlq&g0rCfjSHD*$WScD1q3B(Yb_ zSeX1HSl($H73Dk9t@dIOaQM^|wx902nU@RzixG1M%2>?mc6*!)thPB&HxI2Cjfq_F z1p?3uGk=^odhvfA3~sMZ35V_ep)18I{<)aOsnCgO>mO?@7^~uWxtbWh%_5?TI zDT`G&E~o7Obk@`3^NSd#yCEleOie_5y>l*5_IEB#w6Z*Zi}pD* zqj#6n2|Hn)+wTn4%rM#nV_a;y$0ohBw5V|8YtyRVycb|TM9XL2G{b@4wWrFAQC29Y zEJzAfBdsWWmYHQm$Yk^VY|n+;#9vcEi}6lO%5S3m*L=Kiq2ur9y}g@+gTGsLrX)L7 zNA%uk*3z!IIlpTGXy0X8sq|8727=2!2zPdbHCJB*D2D{~dk)O|%`3l?cY;5S0$$-7 z64Q;pm@`z5+iH`ah8eQ5b{xBP-f2+Zx@Ha6<0~cDkUEOBghZ5Snv5|!Mb~s7vA?lc zW4z#G)CmL`-Muad)xNWJ z#fCNQkTWOdMV=eKNP=M8>jO;doT-Dk3wg%5aekJxQD^26psxkcBvGtfR?g=arGsE> z4uArn03funMouQGW*x%;A=X|nN{3d0SS{W2bYyjb1P+%`#LJ-dx>xzp6Wnqxjo5A+ z0}{G-uVU#g-q-QnAFMoRgwUnmi(K!2;DnMk{EAMTFLksq`2{D`%}{qgyN)h+Nu zcFa_R{99G9fzE@OC*S$*sEZdu>AS*=n)Q5oUBMI~_$|lC02WK-xR>y=?WLlXke0G) zjXC(23GHTwCvGWtHe7|x=?QLg|K#Kox#GSLq~A5=FfwiRxLQ$4T~6B&K*5Co{MP`X z=v%*`8?5~Mg}UYcjoL2Zyuf)=_Id+IfsdE&k?`I`lL<$0H#~7EaksXmHWGefcz;Wl zm$cdH>@U>(ig7LqZD2}SJ~wEVlTXP!?efxU0Vu9Jy+#RBxFJ-h39>TVCX1=*fNP3N z-3>R1>6kd=J>>&N)X3f?m6*lLRwdl9+>~C9d3OjtF`=z*2_HB7lKnKj&ZYVKb=rFZ zA=cdW%NR+XcKv63y#Fu$Z%N;wQ3WzP-l$LfPxgMv&7h}}B)OY`p;okP(sCU5rSf`4 zep7a@!&htxm-$kDrAA(l-&>MWzHL!OJ2EcS5i|2^8 zX)zm`2&ENM&dQ{hziX!SjsT%t3&-FdE-NA1)6p@-BBi|TY`O-_N@ai6AQ%RY1t_W2%F|y}p?1d*HEEur?U4Y49{j)-K>4*xH!1F>M8Z@pQanH+bN9@|ZVYIIG@w>^ z=ClA!`;Q;h-BehK>DaQo(oJ^t;zpeRsFyl`>$77T2pekCJSl}vF=zh(om&#a^H)H0 z$(PRn11nstKMVhl6$6v+a`Z4%WiU0ScCpG{4>B*VK_v-EWF-CP!>W@-0`X}wWk zVV5atfBVPJwhjQ@lx=8nuBn$xNrpO{EYm!#=bEYX$*g3#pNUUzwYcG177#iPG1|*ts(nKV5T6WkD>hx_#5fGD!3^qeeJ#{1k z@I&*9IxMNM;(Q29o(3N-Z0O!y^*?STavk}pq>fV@O@%D#s4cm9>xa9E-MCmoDqJk5 z>@v@wk;&w>M|;68K?jo$SB1ScHh5S4m?lpqZ(8}a$?6Ec#^~^F>QGDPshXO5|9Ihk zV`Zv-H)?O*09^RU{@+-piwYs9JQi6OhUa#dp^JRm+aHt3iCa`FzAw~YD>zHuq;|>z zK|Ox}7p;Azi$+0k0pO{l{np>rDHK&b_Vjt6+^2G%zQPPo-N8P zEDR9BMyIX{IaPEc9MGpFcXY^!@(>?1QB|7Gm~yLk9-*Rxr~Z%4F~S33M8l90U|Zzm z3h5okJ6n^(RfQq01nTFD6+qic(GcEHP=^m9NmHuny?E+tw=6c9awGU_#j?594|L{0 zj>CWKE?9^i;7!10zdzyC7OTjQWYZk4fmw0I617d6iUpS-j>TdHS8E3$6@MD}Ts!?i zFe$|-St7XzvYXzK=wMRJg z$;m})4Wgyc8T#PtsL>BAG2<_}e3H}lw^hHH)_xMk2pv9d7&!C-7}GgkuH&&vfqf?e zot|{}1nz*0nz~l^;|TH4?fRONHN)xid>YX2D;#-}qNCN0^sa)ptd&(PZ)(aOc|wQr zF5P44v%ISf1Nv^y4`1<{nM52vX$=ahx9)s5Dl?L5;G>gvk`hdxC$U+Jntyx0t)NzQ zV`A7~IUXo{?JsB~-1GHN>#GQ8wD$A#^2LE{&|AI47Qx?8DYy=@VWgf|9Y7ntSf_+T zsSO0zSO>+JB6VXvu-yOXcfS`KH&%y%Vx#Yq4oUs9BsH&tPN$<)sQG?#5;?qhW8?0R zt+pp}Os_d-3bUPgMM=)mQmwowAZXb^C2H)(ta!osPKNvGt^W2Zdo-XF=={g+!e&`V zgD2UZ&in7jDU=2?7P@smgAoXN`uOfguMhD$?k4C>qW8eXVXsp(C0f4K7LIIyAMl#$ z==zc8)U__!!(g!%AjY%k{H-$loHdiFS(#u>s_3#6DLfg03kZ(sI9qA~n^6={nhNBL zZ3-P&g8hV&A|RN`j;S>P$Cj?ly{FEmuyu62HOakgvd*=yUGtBWn5J--qEp!sh39HO z#Ob}Bg&vaB$&7XxD>^E=#gfrbG7r^rau!!L7(qM{^dhKQ=U7pl5g1U^UA86A>34gg zt!*rM`UvYH5Ii2>Su}g2x<<%uOyyuj?*+st=Mda5{xmR2GLmlG)6)9?Xls|AR1@M$ zwEbc%=0o#Y$ekO=?0mC)daqa%?L*-p3m39$>0r(aoab|oHQdCf-WDCV4nx_l?>P$z zW=$hv2scY{Ib~(l$4&Noiyy76`A``8+9-oQ{jzRNv1OCRUG8gSvTPszv)~zXsioLG zN}^oZD=D$KIYSs<61l>$%u$djJm5y^S_;oOI#LD$It1mdOekwJAb?_IWE5GylSu|J zh4OHtT5-SzzQRHOZk~+7UBzJ+X}uea@K!tgc|W}~^KRKt0MWx7pK5ixom~OOUF1oz zw%06LF(5$pZ%cdAaOd|rqZ?48)kD><=OZ&+X@t{BPMy=}KRSI{>AyJsPysKd=B+dQ zS!hI!RcKv`@Rvxj^AxN4rX+&Pl#va(4y4(OS?8M3x8?xH9G{#V<68?X#`-#AB=T}w zDZKDiSBnX_$ikj<69DPSPku}(UXDJNuQtgajJe0K10WMePkNqX39lH*O<`;hzb72< z@Tgn|YYc*&DO+eYGHEC{PP{;zNpEBnNfX0{g)z35i@ zNFXLB{xmSYKWQz0izRN}+Wu7J$mKMlv_Ejic61+KJtc&Nn_#Py@ znYv==dlSPMYEZG$GEGvI1m;|wg>y=% z>5YVzYfK`!?*qT+;*FtIy#7g`M=5-9s`X%Q74Zy#shzX*KWo!}vnDHnVT}G+BD_;L zf|gVW$)o1=UYKR4J9R9ZbGPKEk1kJ1^?H-HEq`nN5V}6sG%MG2Zl+Z0Eyrx*L+gPH z7ACq1vU?rK8O3N%FP^yvbqo1zCltnYrx{lm7c5LqLL5QnBb2K)(OpkO>S>c*r1Yf9 zwY=a!gdS!hWTk^5DL>HC^OdXaCDbH(1$&S}zizvchvi{}Bg|*BLaZs)vc*>Sy}Ftj zP3Ec03(`|8(%PC-s4Yi6WpQP$P%aOE#im8Pb`#&s;k$B;p=%oCiV0c^+HHKA5z%)z z0L0Y)D|lJ+y&3~q6`Vh=%8c1_0_|*9ghHq5I!%k5?LCYxaF3H%ohK8yH6ybxWQp;3Tqu+OUII6mKCM<-qmg;8~{u-Y$a_D7kRUJFL)kbB9 zD%Fj{(SBSQ`e}6QBJcPgy~s-Rs*lIytLr=V-)q}l;5W|_b;}?`?y07Rq^CgSK5OLUXQm&XI96wr)V~NxiVs`kBZ*;QO8ny1Ebh zPxuF!-;qo8QnIzCRJ`%ncW|$lfsm@;BNwInR0NG5_M-qi!WPpYmvofNmD0>bLxlq> z5qajWORXjf!^^38v%ovp>cp-PyMwqpFrMGiY{GqE%%-ZfCS$vcqFyDQcRTB9?UZ3_J0az~(xLo1$$_H!hr409acl(>GT5Dy zUXFZyb&!K*oZxw)=Amo#q(DJ>9vYw1eg(GBYh2fm-0s>nL3$bo31LIg`GuURXI))g z^pb^oH_WbTxje5ZL0NbtvQaMY-d^RyyVm=>iZW92a*!y+i$?F|(AZ?4BMuc3x{KG) z>BGw2ij7y&-8y;()`JA-ACrd7zgIWr%gvbb|5T43kW96i9ayNi5m z>tWPs>;>x!>wA(;;H}Dxxk{vnnPtL+WbCHxGu zxU3PGP-S@5!Nbmue~Tgd5SuIUt(sTHKv94FVVlX;e@!Y+UqO7L8M&`Vp~QiDv)$bC z|DAjUH%v}7XGqaqJfENV^lT0q-Q*Bfs(Gq&e(05N&Z-Ls&V$*!>&id7sOVv~R6^isIt-mR6^9+0+f5`_HBy(#N&bXS)J2Qytsr^>W_V z*FB2lJzgKxUZK&7!LTz8Jd+y_PM>aO`yCgLn!e=D*=ny^J7T8!-E*FU^o{TX+%rWLDd5o|=3urb{-+Q!EYO+&v@tejdGFH=+`Oys%B!y6%r z*-lhO&ko<5jjs%S$Fycu2xo3wOCBP2@(zPxb&5D^Sut58d8lsa9(_eJ44TK=Ady(^J%xt=0YFFc==-J)5d#JTN5j>5k)J*Cg@IuFGNH zH)_7=Me%IJtbr%-C!8EzcB8PZt;rvVshS41^M+j>?+WyYvmAH zk+C3Z0yAnV)V0;S0W*h*bDA!f0xpuh8BmdI0G(45HfUZ$HCeFBpAikUP~B|dWaQlR zV`SXD?45!N0F>cPssaY0oTk&<#BTI0HOpr%tkyRDzfAtF zSSQI5Q+e8?&I{>WP~PtgALU6R%h*i$S6pMud& z%K+z&HV{5}nXN%o6Cc_tb}NJ?gV`NG<=lw^QiXD)TsZ|5x6Bg+nPMFC;I%vb>Ed}x zv5=Kd#0Wb>@zB;_;xUyit<4S!hQ6>QO)AJ-3@`Z`kp3IskpK_CJcDf1A0!k`($HD2 z%u84d>Y+%?0{(BG&^(7NQjUXG+{C+yr{S*5PJBK~#Z2u64z*EZhoPtyRh zUmiuNFf>gSjQE{55*8F_tiK?g^@V-wxS50@})54ac2H~r^qqy3rKt`i4q)Xqr&RPd@%bt5tx3?Je zVB>Wgf8B$E;$O&w_d)w+`bgk}!DPXR05u)->TR|cFOB$1-1qvhPcjuw)jHI)aNaWc zsraP6@Nn(PluRYz zXHm%TrM)>YPijPviCiVFfc-fQ?fvab5p*U}@$r>wqWlv(tHOfIXuaaufz;fKCd9BG zu#N=$1!Cn0o?@9+-E+5ry5xSg$8c*gfa`f5hONe$KLn0lX7BX&^(^j-rSA0(h*MNE z@MTTIZ%n&80<8eyk+)#}o)71?j7?G9`|Hyln_49TP# zk~i5br_a2ZHP`M)%+Oyf(kPz#Q7&Tu+;6M>_a=v3Jp^cuqDg?7z)HZ3Wq)!?_mITa z=t?_Qax}plTUdrspS$d`6@%~qBcXdG46nv)MZL+CL#bfwr8#E|a}}q9D3FY+j)a|V zR`aQUWDN&R9*CO!Xai7tp_ajtz_|KiP!Q_wS5fUd?8J7=pet6b%Je?-Pn`87AMC4q z-J;vwJI^B!h>v)ytRA&zDR@ux>bq*EbI#w^Z9s<-Mv#PD?nu0G`LdWqFrN*LP>^tm zCG2kiq?|}%qW(9AwDmy65yt#ZxMT`|MCwf9Y<(s>eDoEU9{-7}P}30=?{Zp!PPMgP z5G@Ld+iOUPOFs5GpKP7K1_Vp4NLWd{VI^M=R~p(j z1sv}|n;T#5@)xFJ9l;oYZ%jmb1EI9m=`zvs1@8aj?#<(&Ui-N5&grNVsna5rq)iK< zvSn{mr-UpslV!-BWk`(4G8Cs2p~aHDNMe|=&M<=sg~l@W!5CwS!7#`&7|ZXP>fFn@ zo%1}e=g*(Ny=Lb7y{_xCyqD{<5DEjs#6Art{C<;5f7scSZ1^{P)HjOrUpnBweBpNu z1Y@PUavaRtKF6s+Rd+pBzD&osh39TNtW_Xt;ZM~}K3mBrorR*a@?PXk1OLcFln2zU zfIm^VXX>Rxx2FJFRL;_-*qi=0>ia)Y{rLA2WxNJO8@KLxSyjmdaza^EX!pwHne=#T z_LgE@ssBZyz=Ax;ab*J3s;wH~c+Ex>LWmHjL;3?yRQa^k3UBa5$I-xeKQ?ae=-PbqpQnV*GzUQaQGIG& z;CgKi3lpY`cMT#;tAU%N@}1hTaB^GTuEBdPSkfa-U+S^$-%A`CqB@s8Ni)V3_3;y3 zENr(qm>wR@%0fMW*Bi-LlvTC+uqS@x{l73ofZ*X%rH`71i$_UH=758^8n8xz@Nd@i zaycC^#2qPim~o&35C;RZ0n2Q2B|PzISQfpf$F;>?F0W$!m2Rpb*du&x!G%(w6F0ls zxzOR|Hvs|=4m4!L9QMF9Jg0(A@yn%MK9)hX(lu({i|X{!iPtig#XVs;?6q_Q|5+%kI|B&vnP1kRXn^bUTSz=i@zofzegxkTUW?D06W%T|ofE%{?LxkelZgPX9_A=i$A%CIam z|CPW6QFmD_OGhJMNkoG=@%@9&Sy?4#&Qzx6aOQFL9fi)%k?GsNfAg@}zsag~&-Q?l z2J@TiAuGm8N9DNO+War}LML?Jt0 za`H>Krb&#frB|Q1xKKt`$t}M2)miWGGB_iDhXFOp19E(>T}^@*dc=X1wQ1aA)IpcF zRQ96A>s@F_$TcAcyfRC$yC1C*?gG0Y69c#4bgqlRh5HCQnKe+|FQ z_t8o&8d2w~m#%1))!H{Sj5Z3*vG|tv@fBnVXMeIt+MQ+y(yn+-5gy;1wRnKVk}y^;`asHbxo7-Wpa*GtVl_TF~i+Szf&w%SRVx z?Fux!!N&sz3hh|R$M8$mI&QO*&6fV9%Pai~XmO%UE@CjA380?{JUL!XJ z-K6Qb(FLN&!vvuzZgDx`sj}KYz)bk zKX7sRGP;ebIcvk6(6S|YO?YUuI?4}q{UB-q9gi{dDS*$S&`(^_airM{RObFnpNy^~ zzL}G(Tfp2qRv+S1K3eLOGzaG`@y7$Adu}D4-LrCf6#yM`ta2*y*P+Gfz!p_s^@_z> zaLr9os4cG`jHo-ALXRUO>3ZB!pU*ncLq*Gmmgc1{mN&`1+FsSwS0a{|9_lby0)flZ zmwadp4d_&0^?A0s*V0>F8LpD}UCX+bkfm2|lk?1Cn)m0MV02x*WzG?>J>H)<3lL(c z-{SQwZ?U(%9AL16r{3X(!?EsMa7iWp-sg~;15m=Mb-!{CagKPocRiW+QY;bHkf7qA zzuBip7S#xw0Ggp;iYQ{M7G^rsAYO*Q3$7gZIgpmCo=1tFxFTX?-AwJ1Ox$Mexq;!@KaIZ+CB}%lyQh0zE)yrh=-5ffq8I($J z;(2a%4W85Gy+@9mU5t2A#$`E*d5+EA{KdC@ML{4*vW%QHka?;QZ&oYSoq0d^Lf$Bw zebzZ(=Um&e^N}yT50ht$PTazj;fJcZq~+>#JibArp=--(@oV1ZM@Z5r9L2r)sb%>+ zY=RlHTGEQ1kI=RJrv&*SF~@D7vvGr4PQmpZHZ&w{~I}E5W z7p29(`o^5`ZL=<|1wAF+B`aga1VjXfZzFlIzGMw>oVYV#F- z1ZfN2{K!Omy0DD1NZO81w93}qtpD=jELfw0alW%tD072^{6n|p4+YUeJ;A_279PA` zE^KdBwf+O!+1X{Aw}XY3I)6z54Hi%&{PaVV!}3u!~FFtO*-t6o=ND{XDawhxfhNx%a#`>s*_ zqaq`bT_=q%8gnzMFwB`*Ac(tHi#YOpFsS~rWzL?xcn#t*7#KhFE&zlgukjw*o4(y8 zrNj3YbgwIZTAgj**xudk-rCA{)S)+jchr@p1F_|c-~YDa0jyUrvJI0!zIAmbz2RaG z5V6%}G#jrO#bjVR`=AJq*R)#IuF3D88 z0n3l9^!vg7h?oo6s#`X06)C1WC0)L(Y3|t?)*nyOlvDaSkE8sa(`?7R=}fNNFQm)+ zgxg;Lrh3;U?VfQ7W`(%EynT~Es+Kx_*3{$1q<3e`H)Zdi7p1&*L;lLvGk1&$X)tlt z&c?8NR-GB3@ZYQs23$_gAfepVe3S%7*W8<4y_$hmCUv@a<&U6S=wIKycy__(;M;B6 zfB*g0gPZ>7QG2Is(-J>Y8f)=1x4deDd5-WG?x^Nh_o@&V=UXnHo46iuR-U5#Uh%MB z^yi9;sV-WrGpMRI;-jj=rtJ&0#QbbOdilJsCfqefu3ce&)IK}l^6=6QZjcnd7j&`u z?FG!{jX@us-|V~ipns)eXz(V27D5zc?@V%%bP!1RT(EPO)XCYOEc%^Dg8n;_4`=Md zMFkfc;wmo5-5k#fIo%s+KZV}4~X7Kq$CmpT5u<@ZVj zF!E*!aq7H@=sBgwXUGuQPA>hY`n#33CYi&zcxN(IeusQ>uQ(#AbVF?><)E1q8y_|C zC^r3F6MG}g!$(-X;p~JR;_l)v=YVBq^OS<-Vf1CO_W8-ojH#C4_^+? z->*UTq#1eK&Um}`?Z9?iOiwcBEnBB9g*rn!nA*L7_8OZwzHesT!I1ZxPw39%8;B5SLD=Td~yHKk)TXkYi!bd79nw#Z0Q{@-0jD1eGT&naa z!H45AvXRrVXAtj%t`$;p!@0a2tkeakwEoXV_6qd@-~_l1T1lK8P5;?m;@XCb*Wp_k z}b-tVgi%wHl`45i(*6bz5#!>$`s$XitX!Xl|a-CZl$QG*eh!|EhZOBY$q1zD7E1!hfzZ zA}H(am#5O%{tFN-60fYv7J z8m!#2^kL<0rHpQ@TR+PH4j&LK0rR)LB6dvMJxL})Ldo z%V&a8uFYB*3oTQ!@3uuDUZ8iCBUXIadSs%PPndBnaiv%W4SnJXyT$lSqe5sGKRK^< z4r}PfB@Be`U>v;nWq6bB>fAZ4D`ByXW4?Q%yuAgQqLI%#f9z`?$!H|1^mL<_7L6th zUdxM3(MTaJ@sX9X<9;krjD}(Ny-T$#o9!4umQTaUEYwD@jqZCvEm10ty%0r^_sL0?F&DZ@md}=^+HR%T4(TH>D@> zidzjIKvnXR-6p4R*k{w)#68e`N~bO7JYAzq(BTskd)4hmH*;_LnV5JJG{q9lwCxAy zQaq0MJ8UP6_`JWK)jZR8xY|&bea$Qx@@y!RT4A{n9TAuu7SaY!V%rZKyN;}$pu>jC z$kNa(_lQ{|>a-p6_z7}YK^A6C?u;l1mEiApIsS%E$~NAbmLgynS=pW!71oeX>Vroq~n!^!EKoXk8{-XO+2)(Ec3 zh?@I^WiS$9&sA2gCqvvFRqPlp7r+Yvf6X8;+|N1AHspuM#PxP?bD=dJaNXO{n&&oi zRq+xX%oTSMW1gxuUb)}0BX{7~UhbinOR<`mzl7QpkTXtFh~QDyont)<&%<6T%7{dM zz9au*U20tUPkG)%S(E=b_pd9;;SO}=$(|AW-(_3pxh2-TU3&<8B>xw8hJjyv>NvY< zBmZBYWkfPBo3=xE|Jv_w`QKn=(4NNsch%s(Up2ttNPW%2>OHR0Uh7?4RHYDKTr-K; zto&#G7`qG-;OkjNAv(1u*!DYqY|j5UBkP9u9cO!$NW-ulz(|`#xE)Lo_pi* zcok*pu0dQ*dF8PVytuE;DsLeZ0WMkc7eC!}cN*69IJd>6b$$d2gH6nh_xOHz6c%Ug zvFl{+^MQArZyi0d=bF>1@-MHIKi8c*8p;ng6Ul8E{o4Tp?vU@^+t_3;eM0&JywLS! zeFCWixJR8#V@)U&YG;DLf)c9yg}t;I`&CLuF-yjL6zYl1%yjJDp|R#;H5;@;HYrEt zagr4)>tjo6Hf)W*rM0Zu4sKd(Dw;*)G0m9y~dX#B2 zOeqG^)4AHlDZC9$6Z8J8TmH~cClNS}%fNMfVoE+CgFm`kS$VYT9+6nMF}dP;1$af_ecA&zdTJ_TJM$ zIWrrfX=n#mOPByIjH$ZV2lNCrHCF-bBNeaY@kUMd=1|!7H4hFr>dW%ZMSh)CdU3G@ zS!OU8YnRzBJG>OTAt}ZU_=odiJv_2!uE)}knHqbKJ{7>$V3L9S#FcwjP~m6H#wjP}cxSa8s|arh_ZmbBz#|eFtL(76sz%W{LGmJ{R-` zQg8pLSpOPv_=%!Ii-dvUh@>&#DDTh{i6J5c^{<2_yT%hZcOwREn;dIn3v1eUy5O(2 z40TQINH_oOIlPSajYh2U*wG!1j z+syvvyA+}oyy)g&U^m2Bxk?rqM2A{hx*e{%UbsFuytvxXZ}F*aa&hA>IXJ?|;bcyW zq{CkBt3-8V*Q4+_TQQ}H9FysTV{6m5idyAOHCjn9t zg-CREUHMw53#DWgTnuWl#31evb(ZrJ$hK+-)EZuR+88`e|N2dS6-=l$Pr zZ~{mv-fAGaBOranUR2indYta<__>i+o>SACiFXG>;$!?bI3|h8L6@z3kG8-samS?# z4qpi`^woSj&0c)p{VF7<+7d@3s?zhpIHk`0{|+T8mAWU{Qd`?lI!sJvrc=Cpz?1s8 zWCu|jvaj7B%ltHIx*g707-UI}u?`Nnu;&(mIm_AE4SG#B zKHoxGiCyug)=EFdXYm|hMrO{_qB8k|Kq@gnlIZ*%~n>t!|rIrB4V7jxcE$GpCbQZ?weNei{{4vcI55@^k#L34QqM2jT(< zw2gaCTv{qyrQ#i1pqMOmH)`o*9D*@dO|o$M_KJUPkpF#pk*?)RC|R-#GwWBN5_(3g zu0)8tb#E{2?{D#%ze8W8zn1ZFdwG=Ak;qlI=GUY8n#(B+G}8v}qkEnt5JPlL_Fq9# z`p_K%m6`9eJhkm4oY-8RgkSTr%hqo(PAD1~)yZL?8kG>8j*5zf*gL!?hvlqQ{lhLF zUuC$Syjjvsp)_=c!+9<<^CKWGgbVLg>ank{%^&UXFiwI*t9kWr7E^JJ6W*`c6otZI zUaqeHgZ-nF$>Y~iRyW$iwt@$~gD}P~fiBARIkR|rmt_6-Re=Xi&n%T#pqJsWd%{i= zbp|G>U$yNbXqBv?<&B%2C~~DaDxSu%cQgI>D0GL}R_VWTVlWqLWK!tcx_HxN78JL` zGVIJ{oO%uG5%}SL0o!G>fq=Mu=YFGIJa4kjtsAp7Tz5(*%dh(Vy)YTBt|7_(T~s2M9zl zm+RjN#WIF>SwN3s66S`dQaX}5U+NBceva*!b<9~@+#nskY;kY$J&39)or$PI^4pm# zvwI7_kEwveNih4IxK`gC5BhF{s?Jna3z79;*ONL z-qcj4r8Mb8V_mxUiYf(%OBv}wobBL@4uxF5?xb&P@I4<$quuY?%jDTf(~2Rn+E$zy z6QOZq_a{Kd*O5{pGS>-Pp9NzkyUY)t@X8x55ORg|0;c^PdTRC@FN?b_UIFbo=JV+y zqrWAMqet`!Pa@r!3i09v|NP^4mx3eoE2w|Kjv77=86&9h$?xM$f-b5CvU@mD4b}(!3dzYx_Sv^HAsvO!>hAI9+9)eW!9|vQG>=% z65?k{DEkAt5aw##RYC4`fy6glg(mE_G3o~6Q;V< z*yZ34#wM^kZ4Pj0c)ncOQdL;*Y|CuB-OpTd-9#uv8?05~b{?WEL8E2OM()ZFN`M!c zZHJ{y;t_O5FuBfaPftzLjrNx~ji-xBvN=|jZ#5{278@#2` z8lw#HY;!(pU(npeK3e-*l#CRq)wjtPI`n?*^l9p5aIz`*WKMHixVTnR5i7zDUdoL= z!}@rA!Fb9sULIwYFOV4(B^5N6jPsWY2JY7y4Oh0jZk=6}n)5x%$g%5j_Q&*gD%N_> zl9HIFRr~x9^#%Z2Em4nUe}2{Srl=qxk28Dr-ta4uFP2i2wDN&gr8(Iv`Wb`4M4*52 zU#;Yfbm-)wfm53<(P8|f#F_bbzJ8s0M962kvFwyR)#MhqCio6H-L$<;{FF$I@cuK+ zO0HejLc4}KZS>V?GwK2rRPqc_SX{Ge9Cm79Y#L{p?B<`N=u9IGNT8QYL>rz2mdjF+ z9_5+g5~?l*LMN$7%jm+9UUiL!WIG)}Qw?tki8>&2Rp_@RIlA*`*n2dLEJlfgRg=#ORG0d(=l<)$fR- zeVV$?1AI>?HhG9%NH^nvIaL9r0s9r|O1G9W`B5Kq-{JdIhA!9!h(FpaZa0CRbZow1 z*`=!4srBoPUiRw2`~CsB&L|XG!4e%F7drITBl8b+pSnxFr95E1e|yTO`O7|3u&p>Q zFb`W;Ca?J=e6Jm2rX`u)+<{6RRb3hLRQSQOEG*RJ1==xg4P`Bqt@b%6O{O2@6z-581f@reGmm<``nV1(e3Y7=Oev1aefKrC z?A5bcr}#&a(MeCx8zhRUG!~`2j!^j2B$k+mTy^`oz%n^>=$-SkVITP+&nNuu%T1}1 zh$bL93YND9sMtyUZztvXN9eKIxD^hqGm}=wt%)cspC_`fP#j1gsA~^yXG88Kt3r5GRLAU|{3HIFi8B36cf%iRWt^bn z(`E)k+z}Q!j#HMoSx?e*Fra)18c_)6p*7z>ZDYAFX4Fxc4CtPukT;r@M#YAEumR#S z1(}AKcbWLFSFa|d{~}NSs1A$V)PtT#0x!mw*cJ_Y41E{~0Lu=zi z`BSdxFZtGk3977;g@_WkyGEmB3GrR zjqq)}L*`kTu8WQG03iEoC#G64qPy|WmA`Grh|n4tzi@8qO)7M6yAuJddl$^Yzu{q! zkmw+Zbx+U=nQpv%eREgfgS2#;on;vX3swH&f}zFI3xPzbXvw|mxu;b1X& zob_8Z5bHN=@#@1_o;EGdCTjw00CQY)|JnIqwoEx2vmM!wY*hJ1)g+J227+bR_ku@y zpO~@CwGK0TU?MHgV~cHW(x$1J)e>vW*C@abh+T?hhI}RCcr$L4`pEK(tjrSGSLvgG zrwQo4P@Hr|%Q8u~ZgP9R{#2}`*}~f+4J|Pmg}3r!V#ryYhZ-rrM4jLTzCV7^*dMbG zhQn0kgj8DG3CP=g`jx!XF!xrK*cbWUTaVcC4X;0Db%@3z)6K`HI|m$rTp`-o`5-5Zyb{NvzDJ%o}G; z-xk^sowRk)I2t`Wi(-V3%cEW^dJ;?qk35;)8RZ*vFjGSCzR1~8MMVjL^z(6qA-wwl zl=mecmxafBtK6MS5OB5ljD0vH@h%I)#n~)3`j+}A7J`ec4i!?W1V4zRl!4MBph$CX zbwu*@y)GMYIBd|?dW`__Kcc}4ay@}b(oK6P1)6#Nz zbCS`wh5I``Cg^=FkhXhz8X>@nqV# zEaQxuwLeC{4?UEA{YI$5MtM^^l4(sR8SB4w*YzOYOS{MA2~CzS!IHhrg3_d3^&Pc< zHO<|EW~mG}5T(i&Nn1b&Yt~$t36)LNE72>n#Q+s2NlpS&ld1g{+|j#%i9uR7L!d9DDSSf9=cv ztQ>z8)sI$OX3(1(PiNei7u`(WezE!hqD51DU_uiJgmiyO2rT;eT9@jWkwrIb6pcKq z&p<$>r=4c)6X{#)4KO>Ue{Hv^SDq>nd>Xu2ylqVHLz3men+J2CtHRM&B~Sa!O!WC2 z-)MNkJHTqXw>h{VE;P{mVP2fknI|-}?Krpo%xdD6C;TDTHGNgrK^!P$bYc=B%_;XiC1X(t6Rw( zk&f~V=ix%^pu7lKx07(KQWd&D{DrLZxeQibER`yuXn!HUzu>C{KXRtj{3y{}m44Kp zBcNA2cQX=sf(-`dsGM~`w=l_1_=M&SBUH37);0d5=S)HUMmMSG%-_8y8@CYvo=_$5 z#HLEu>y4>(PCBc=xLriR!?cZ1F!R(mYx+c8Jo%P;v$xb+G^k`?6ydLu76Roy^(;#t z#>3Q=k~mxi6Hek1?--)k5O+e^oAP?nt>vTKvI4{0xz)3iFw|41SGzNM(OUwgC!sm2 z;n(7J-*;v9#@TGH$5-3uzF9QrwuPzmBor+xGqWqZS=|BLLEV*~j0vd}iHZs+>e*gW z#&4Ir&`9xu3Xgt1k4hVGAeOkOd2zf{+CxN~#{i|~jys{bZcf26`>2LD<>|Wx9MA@? zc+l7DG8LDNC$V{iVKgrfiw$ll_0fIci`8UZed*E7Ixe^XV6?`pdyHKaV$rU(BCpw{ z=gg36$)=M^H&?Dl3xw=%H2 zGueYBo87SGg1 z-GWE-qE5_{(h)1gapb(&?oT=lZ+@2nxAbl)CG?`INIg$YI&o)ip{wFBc~;{sigVt% z*ek7v_^p2E8brzc@VjX%cbh-J=ml~jri0vXd}vfrTBAWheGC%4o7Z@FHQ#LFUgN_@ zf(O{6{pT&d?Hp)3r05-~R_ZB<``}pjLO}=?oPO`oLNF_VlKlB+=))L(V;1E+8@Hen zP5SKyt8HPnLu&%GKi!0kB(^!L)*AN) z@^aQsBUmdeB3j8UDg-g>6`WGELvO;=CUBqZINnET*%q)D$ z4lY3q;4#j{#3yRPt@SmgQ^Jx@wP~Q3Wfw>T*kJR<9cf|rn6h1Cl}M3NH2!UiY!`+G)ypIB^`+)Cj0h>i z_2-kJAi{L*^1}*}D5Ftyr#H@nv?t6xREHW`+-bJjo6R>(a>`(oN04Ud1G9@{=PfIx zSF9{7&KKChZI#w2&x1kc`tNntMK}M``1ueMgaIwYu_Q zdHCb`ZLi;JwHQm5C53#H(s!1Xd7^6Ls{aE6Tk~Kv*tU%#@#IM^#9JQX5PM8D22hds zD~~V-(Bnkm_Q7ITuabP`xu85UhnoT4Ur*0RlRly%f0B1Fxp{=w7jB6@3 zs@p-k61tAfDI&6S1=~RM{iMw z8=eizzFAzQ1qNceFjp>cw@NpHT*$Ad|BL8e_u8X5(S7=BOecsjx#aFUp$u>8 zVOh55&dr+=Z5GKT^xq57~v40i2wIX*2SLu#?!1-M|^(_ z%yw5Pu*@}gaOTCamIx07)9j{c9lpR~u7Q`_aYe(oxM|>}9HOr1Gt^ASzZ>L(wVvVH0^%D<@cd80 z{y!jS@E&UbV(DvJ6f-({5;bMWp6epbbqnDX#Q?1FXnCgg1|}l|p}tpUEvyKT-v4*Y z4B_1L83LEfm(sk(x*0BG-G$WvgxIC_=w~OheFE>7N~IZJ3V&Y z#PuwXy`+}mpQl!txw~I=v2;1s^@Gd!FZg-gftT9<#L)j473mgxT(6Go5j%A!hk=|( z^{NabbglB}0b@);j{~lBs*IE`+x3I2`pXv>)qi6TLhIJ%vW~_e=@&0v3>uU%agETL zEtwr&Nc*q%^}PRA?(HAWI#5nrPGgN&775(3`P2m!__?Aoh;iX^EagH|J|x)XJWxSKh|M3F_Xjaohw!w zwrpoVi!&rPPn$ExBvIjda4 zYR5QA!&YK;*zTBKf{VIf_TTLP@AR~`JrM8l&)$EOvg__Q9DG?z9M8!6S|Vea+vP8Q zhO%MvQrkOL9lp6aKg_r3|NXp1?r}9SDV8xIR_GPe-?8{BWcv_W4)BnEVa}I?7(x>OR z>x_|&0`i4KWslY$Ezsq8UMJ3bZ*a8GP7h%up3l#AdC&C+H3PRt4xZ`U-K81%{6v6` zB0);!i!cD+8&EFJ}iC-U~q zPE)16WtV`F@^$ChNMg>rZ z^7+T+iOXlKA|`JCdFYfWn_=FmfvdGKa%O0BDFgy3&e3rH7&;FVOMV!=R^T%i8&u=B zoHE^NsGdVHB*{6N-~ROFk?jfT34qSyL&ssOB`tQZD0d}%z67`$487kenvAaurmt}0 zGeSoF7WZ+=xCV5~dpYj469p47=PSh|3S~2|t3YJXHEVqx(KfugfFFeD)BjvT#0Njz#_tg%lZPNMrnrqZ(I#@qM8&Q`d zaqD=bMI`ym+>+!s(z?nYYntCQj={O*05K2!QT_OI#O5{l;HFHj1R+mA0F)`1mhIfu=zA#iZfHY&OaV=muzBh#ez)@jE>q zy&nU8kTT+O2Xwv*}E67Ja`H7$69`_VHSfJ3l^2smk8P zW4=F@)Cf?D@;A_eCE8iCd)B4{jVF zYIP^AxL3W}B=55PZamDaF1&guPTtqRg|5UG0*Px_w9af*+aj(x#Cb=;P3DkVIByP< zSM5x4+~Y~oI4z}XYf*}eYV-3S65g$9&Rzsf4Fgz8VM+ zD!fLGOD;*9?(y#Y3UO`g1!yjUNFN`0VF+&9%@US4-HtHoRC>jHe_PGJ!Wb6brmwL@ zupu8n1$-tE#2EBI4>O}+ag;rw6y-yYORwl#zj2Gn1di-+(F4ly0F>jSEnN68oLXtfFAvLMzWlwzIZDH^;P2*Mv^flaq{Kc&R3h3f@`JMn<`Ka*q z!v6dKrhgJqEcaT&qI$vZ?BP;CSvsfd$8-SxuAA4G=(FtKGyxb^3|0R;$s#}PE5f>h`7jZ|PzM(3WPqaf^Pd!RyqV05cqRrm~IOdt;2aGBbUHKeceY=~z;s9C_4% zU$=-cXIDRSN>`tBB$FamCmpr~tW|&IX-6((pM>L^?LsI%rcy?HD40w$9m*{604im? zf2DoQR;gp^0V{!Q-icHp`?O|5&K|(QHsG>un7BTh3)?GSemNqxW2Pu=pHb(VyBF=s z7BjeS)Z1eMX7|SNgyc%CY^wC`s-)gt9a9nNUhwP@S3@b6Zgm_X7I>C9zoDLIUBef7 zTAL8k7LL?LM%l4sExJ54lS@bU`YfXRQv67M3wN)ntnew;!e+%JdBOr$hq+1S=PW-9 z+C^MiG6Jpi;?*AKZG8(ucX85UZu;M7@Anyx&35y)m#j89aH89s{@_i~$V?}Rx%vLB z0mCWAhIiW+SJj)}eW@e-M1Gnlu`vwq{?fkdbBAc{>Ol++N9apU?^szM%&}RIxcka z&0u7PeD_2~G?EH2CQc$IpFPLBx9l>@IYr&?+h6>Sb;8AOC5?s{o;>YJ{@H-B2~Snl zDhkXF4*-*U;IE|xM~LdQdI7Ymg`|l-pj8?vRghSI$uW*ts)Zm72U! zI3KtOy4lRt-j{f1t_22*&E+%G-Xmv4uG)QWj^bw!GfuxG-eJO@+vE4-=1&=c_KZI0 z!7rnV0&}Bc6DEK;2FEEEXytAAatZi)#))~;k#M3Z+NWg*5%BBBQtjde&R(beKBZi( zr8>R>z1~3E6urOHr+S5Z&4v7Ef5aZ;PPm$QDz!m01^s&QdQ8kqe`P+S$-U;$=#YwD zgjY+Qf;N^Qc(SO5xk#V^erw3z&=4<@|A0NWbcLsj>JV1$u~%k*;T_XVlyaCaC!qUs zMB;G8bolFlBR4FQmkMW!VNi=4%CzXpjmMDvW(Mv(cJ<`}<_A@ilTu0R39O2yHOj36 zW`G2d+SpW4PGMX!u>-y0H`)_b05K`xnMrhg{M-Up>PmHx^DU;+>+=kwE=4g;aUW{FrtssTHVX{8R2+H=8CR2Chy`PCN4e?o@lnmmg%;!=%h|; zw#Ai)9WrJF(sfwqI%&0bZ{y4GGV4NLre=IAvdPA-kthpB&bm@kUM5bME>N$2YUQIg zlnQO0p9!JR#au}Km_21t-BZ}F%=}*OYlb`RqOPQ)@G|zKMwH(YaD)`NEeeAPs6 z{$%?6CL?Q!o{6xL_Zhal1(;S>MTv;)k!);6VM!VWUSrYH(F!#|eeB2pimcPzo5t;J ziOA`g>|CIYyLok_vr1zskCW5%643&#PXKieQ1iAF9kXbMoVdw)y4H!fFuO9Ky9)36 z2KR4xL#lFum`|l$d-z7MIq)Lg&jj`$5enTB&D&XY;uQvXU3g4huMvTWJ-*ZUz=-7) zz>yFBeDPthpbwu%_7^4VTH%f~7+G25ol`ZCnl}D0Hu01lbe)`;--D^R{xBk`!h#{$ zVRU=Zhsy=mP(l6>Nf=E}KL?Q(a@OuMW-)fv8IAtghbApGE zS{z+g2b3&uPgo7~%qv13XFI>KnbEwa6F~jwi3Nb4g5$HU_Nf(L5kKtEZztJD8`4Yd zbWC7O5je9vccO4*>fu+qCORM|IbcthLIDL|CL#VNlUSwX+Gz#GwJcveXcll8@4_7a z+Tkq}L$m=>I9=xGR}T@q{qin?n^n&>wL6oy$9Vp2rR0zf)a~V}aiA049@$x5d81D_x`J%sJj^{J0Kx&+X$Ip-*7EiZ28GEI1%x(JOJm ztKq|48*7Q#XeBH8q3hcmOs((z%aT?MP#SfaLv>^x#jZ8YQEGPk6Jw# zuj%|o=$?kSmS0`uJ2|V!HOj(mlwCU&mQjv6e50Lq3n(`j1%kM(9R;R1XqWI7z+FA` z6m*A73K_0TZ_e~zJV)L5Sk=vX1X6wcneiJg@JAFi%FX+jmUjey0({-8^lB+?;U%h& zn*P-E3)|^k;$%0=H-mB}9E(casqEKA(n^HL@go~BPdv9UewqcPTeE_xkSwI3jR&Hs z$TN|=%-z`spa%(oh5;Ii%H*H(Evkbvll-}ZowgGflV1vUd>(4~zD->xXJ6E(m!_p= z{R=1)1NT{=0xe339~zR$jYWkvQOTGB?FH$v;N&Y^1$%_z(3Zv}j3RH6**WiOEEp)6ILEfsM_p?Zw^G#F)L?W`<@Tg&-sfQz(_2dJ2Tf$&Wj#L$YsKHP7pwEo z7<>mNnIWfJ6N8X&&!^bXGp)TT%}-t=%Kgp@TtMh};~ECICopplot8P4hofA~Cp4xM zjWye64a(YAgDqV^B{bJ5ie}8VHag(gp4kgUA)lnCwt%9tzFcUi2$UfFJ^4d;;Cn*_ zwfv}lZVt`-0|v(yvsu7Y_422M6^fNBCjhuCCluzvVuZU4os5%=J2sk zvbT|AKCQ+5oTnZ7--=5-65E2DBU;Ys@(VkE?GFKv~AJJH3+nS&~nt+b`hw`YOJll{q7JtHr}! zIXfiFW~ZOj@3TS`N1J)S`;{Ij)vAZ%*jF-Do3LFEBv`I~AN|Ffc#$lG6cLC_Q|6_4zoAU?m+tQJ}BM8{HauhMJWBq0}qS+<+bk z629`hrYF~Qu#iuiJqp_mZjZoue1eL%G$S`%Q@1=MqNlh~Yew_MV7aRPq(^f}WDw@# zsO8}{7xk%WXy?tefuN0BtLJ9jJsJxwh%RYr1Wp}BO~}$&b0wL+|IPxp1x{2VLCg0M zvZ+U1suDK+usA6@NmXXP#cZ95f-${BX8`us1qcpqpolSA4spmSLPxdY8#4Evi&U{qfev{)uB+IAjK%4yOsS2$?=)F_nePd;AN^WhsAR^ z6=vpls|3g!w}>M#XCuPL#gv@xj_nhQV=Sty)w%uJ#*0w?Ge90pt5B9R`7q=NG}ODv zdG0svhX9-tdIBn$ld|iVwMUc!wm% z9BD9%eFlBTmz?>Y^hty)O*VUPY*XC44(8o8n@mieU zz6fKhJYgAZp#!NgHWDKcAN5>1eg$X?IHf!`5(GJX#!WRhe=Kh^L?QZCIB-y>E_ep8 z4|7#5nlh|?=W}K2lU`o^21^^XY)plGoG9U0iH;rJP)dY&_--r}^cJb%TR-MR_j|p; zLM=7@zWkw^T-ex<6c%8`A9cgexRzsQ83we~H^x_?)TU{nt^3`}aX(u%Ni@GOOyoTl z3XryD-IiMm7^vPjq_9kI>~AbHK7PM7^OGQ}#P+B7biP{eLe_<1zW5AH3>|l{oGCm# zaFlfG)afren{P<&_K|vOJPb4SsnVE3eM&lXv^|yVTZ2tKtUQS{De^0m#-`v{eC> zO$exn2q8gcfG{4VYEc0fdnw2!VJ9SEI6=b*JA@EW5dwsS5lDbA-s?tu9^2CQ*FT_= z&g3FwAc)zf0_m|Q)DNJts0fo?*!d|s&&uP~cPb~eCfq9fHObOSA!XAN_ zs2&WXK?5-x&|eF`bd|*i%%)-Kc6e@P5-{7LMGuzwr1B@F8v;IOR^PB zy1mK888Oz#*7LOn3LE*`g5+c2Wm*{z2m8W}X}!t|6Va#a{8r(01u z1tMP1oJXNAAGc+%cUHBBBeVKefPUbde2y)Jtz6o%SX$?UAg%mow!!7#TcbOw4{T3F zS60*RMN3z+U2N_-Ftoyt(-2dGNrOdFuy98XhPR@T6&o(aZZ7kacqFChWzRpu z-AU4gkh*kh`E*ti63eJf?C#f2p0?1;+qJ=5FDKs816`K(gp0KlT~dV{XN+B|*%LpZ z%{54`wTXxH>-}Ho4JRkRYcrs=`*=zlm!-VLvdPuvB=*@BwPFZ`K%wb&J^fb#%ye1V z!rb&PPUgO$Hy5ttlAY`nJtZy3(Qy!$@%jsOW8~_i8S+ch4f)9=u3uHK$~x1BS&cbA zUH1?9JgaY`!f%RaP3paQy5Vgw)qPv1G0gix<eSMr)LX9e=H>Kcm=>Khmz% zImEJ22paIMwks&@hePJEEACM|8XInx-0lGBazPkf-W@Nyex{i)df3B~ZQfeuGm^Yw z*zBI3_uwBho24EPWnY>lCaMw2rMBjjynTKTtd;ny<@KJ{9Z;d=4D~;HUd45TK@2&@ zV*{N&Gr^TlV!-SlZ%&&(Fc=6ce!6wLRoZSp z5It3&t~^a`55f_i^c>RDHd{L2aq9P)Wd_ojNu#I-v^6<(DJzHJa_uR)e?hdaCr|PE zfF@(foH(4r4b}HjQH{2r{foz>x6$0nBDD*_80gyJJubdNRb zu*Y$4^cr|>t*7EmEnB@-)}^Pq*jO_jco{w@oEo&^HOLXvrU}$mf1MIT){#x6H`{N7 zZ#_ZHQBqtc1LsWLV-7+r$D8J{vpO0)l6o>dPm*mcZ7Wi=<2Tt@c*Z^=QSO*ih!(&)%oKLAqwwSHk~e(EW4KI}(Gyy5E(tF-tZe zg|Tnlk2?}oBYYJPn#NF4?UNH76?si^SJc|?Cmq8Kp;g znTXy^&KV4Eo)G5x=b74rWd5{Y z>4iz_#lv{tb8L(Qm8@M_q8xG~m^J}dM1H$PCu_EGVjUHIol%=yEwKrz1AOSBO0>Vsj?SLh?#OVF)&;1;;6O=lMmjKfdNE1)>z(+K#x(8p zi&-G};BWdS+xM%`6?;`s5yM{tisJR#5v-`SuYXn`(2z}a4o8&xm@ZNJu@u& zt@@UniN%Jwt~y|ta*!l8MB{SinA@P+>{;4mqOvwmSR@T_=?V`%%7K(z2ft|9B3IAP zz9($ebg$|35q*hovQXYQ4W917wvn}=g#E^dBKxy5Wbf2LFd+1_;9Z_qDjObl@SW5o z@X8W_=ct>LJX*@R-uln5$^zB^_AzYuN{g3m+m3~^#RB@^vuhJ_p6K;fXnQC~9k`Q> z`%(D|H0U2ho5H8g&OsF^dA;pt^s;_&E%=eto6ECtO20=s_8|fKcTj-m$dTrTiwI%fBX41g@PawBfcDu~WE$yFXPDBCcFlay|QTH3150F4pa$?Z~kF~qM z<-)~#8t1B^J)=Wwpl7X_d^VR6m7x0HThU-t3 zj!%03r=_`7T|Tt+pS$0u#0Spy=h#cg@RBW62)tN$kx5}NMCK^M>s4@IDKGETqE%#T zzXxr}8a-I7y(oL)vl!=j?-*D7&@9>*gm>idJL@40_GSizvjaf+Z0(`(A{m1J+oPbXwf_;)G}xF*t4vu` zfdgE`M@tXqd4!V1U~}eq{0;6<(8NnwJdn?w;pbuZRIf8OSsW(;cM&g}W@iirJNjTN zy{AgCQ2x!U;O6Ap8|ft)jpXP`^l?bU z4qC_Oy%|9j{o;$=3L0Hbx}d+cwB#`2m;ZBp1Sms6J`&!C_5`~;_{oDd!bcc(=Pd3? z`M|_D>M%LlB5u3SWZ#bQPvDA{WfVdR4kg}W=*_8Jx9*O3($u8j}O5X(9Q*f@YqDT7KFS~@K&ww6c) z2@=2dNf8g60ssDvUpQlB0YjWYa2*xO=uLGfGVLyP+>Bg&X+eM~F{suu@IxV|1o@y* zO(`o#SQymdAH@x19r0$Bh11-4-0(Iy(>|Z_7X>3rjNXcmCZNB$d|-`3{CV~~xY4-B zubT4uwP_;DHHa>#n`?)~1}qkPO8(89_rYLzx*@?!B240QQs+0*~_LVZ}XTu*^zYUby&caT$%cenn~rOorpHOvmp z(P*yEPu55N5B@>#dOO3&1^mJ94&Vr~o)*7*sR+^iB+pBB%`4tJH=sjJ`==;XI$FQL z%iY-EfS%Fx9SSxU3~6vX-%V82UMR_2?0i(>4d|$RFafCPE#GO?x^c&{nxA&wGNLw| z_DoO#(~^JltY=d**oo*#eU=|?i7f*);7I2@5BJVT;moWgMhM7DK-Qkcz^B4V$3%MO zXs+hmW&FoD=BJ0oqZTBa{2}R_3uKnOeh_%5mRZJ~;HWGxnzdEyS|5{U1^Y-$9 zYdA3ae?FX^6of?{r^iR6?aM=+*L8z^hWlFtDM(RNV*c`Fx?uP%d5*#=eid0jT3+&H zko>&c12GyB#zJ_5v&)i-HrNF=!C^sn{@E^X>rM*X(bkgf z@sS0Uh{cCpFj|?iFG4X}2rCTP2!?oR33e(@p0BciK+rKew3cR%!E^N!xjCDr+R=2GQ^88Pqm-9^jr)^vwA25^n_ABhm7$#M@!xg?v)VWh= zeg7?a;f(rM<{gr~*Yq1KK|G*zZDH{+gj4&AAdG^n8OsCZ@)+pQj*eN}Bq67EB zrd`~pW6@C--(r!&Lk?n-k%Vt=?vvLDyg9Sarvyw??0D)5BIwGr`VH7c{Bp;rewnR` zOUKFgqJ}W);FSAcW}XaD?_#(V{l(?d>_kD6huR##g;@Ys3;|>+IRCj4Eb#n26Ix|j zk;5SQ<%-}6HN9=No44wp%PJc;l9BA+Ykb@*(H6yP@>g@MMK1HW@ps^uh;xr=O{_To zat@N0CsAcuQL#7lDB$re=^ihI z{fI95t$_9Q@zRS^D+8=7ayOI)CSx88W+k;KGM(2ysboRK##RWYv#^L}FcbWxTrO6% zbnJTQChgs$vgL)u9F&O(25xu7h?QXZ3K_M4-l;)kG9yj2Fg2xNFKt?EhQI!FFQvef zeLQMp^5o8(3l3;a$f6;0FVy#%R)IUQJvl^y#ccMIzGljFxI~YxyWf-0Nu`T)81#N1GjuoCHt!>{x6-8%~mKA z`fN7JHn8b>_*TbUd{9@XAH7Op_Rf!*|2!#pKDcl=SD}5w^@8}_CXOXtCd&N8>GdX=Cms9`Nz9TT=mn1la^F_5X(g{iL|P-CDxd6~7mW(Sus5)(>1KPnfs$<*yYyZF3xk8(Pk&zn?Ic8?(qk z;y*6mK@6`To!Lx^V5;7SfhW7)v0$1+j5n2zTl$j3V1lFWW|>jFo1=ZMnDR`nvmucm zx!*pFU`+QO@``b{o!u9;M6u8`F^9KX!?fHcFEEAfn^CtH2H^zTpP!FujxmF;Q~gav zbi|xUO(mY({~NCr)8`+X-P@5Zp$PNmAC>_X2BUF&|Hx+N(iveDlu8_=Fcq?Wo$EIH z=>V@%68Nprdk86f?Pp+@6LLR|{jM~X^vx`IG7!^?^Bi^NFl(vnLt?^5yWgif#UE48 z5`I6@39TNPrrY+jZtgFP{o>aO!>7ce159|2bS<(^@+Y5)`lt0*PqzSJ zn#+8{-`zH-fA-D_YcC$VH8Iyd_)6YH9*22M|NfG4*0vEfv+|ZKmNs7C)N$S%c&6sD z=6@=Oi0V6T8Odr8Pbp8W9JBiE9mdDc7Q;HpGv1!j>X^avRg~d}RVVH{syv&0$8N0S zf+z&ppYI{}kz+HHr=x=)07LB%i|O(OTX1k7^v$`2a?_q*fB*5_`3uWnUPH#8n}IzR z=hlho5uHeC2@%$1ZShbvHcg9*?%KV~MR{kL|6~DYLq{&JSH*UKQNZ}2_>BjcfhopC zhLaD3UuRP@G6s^eZ2(Luk5REMWjCpvirm%C+kYhCh=F-fy0xx_hrI5^4n2UFM z$G|K|6Q-n7^_zNn()ejff`ltet7)4VEnZxm+TT|kP|)#wAwY)zSm>CjEE_p#xk6ov zx!U~U?hYl8*}~__Dk^)qhb>;(aKG?7EnGv-n=Oi zsz!W2v;8#CWU!s&Y6e3ocJ}+5vwNSn3J5NoFkN#(|C#RA359na`I0z4Po zZl*fc(ayN#9o(b11yd(08$r~w(~RzwTX`y=Ey3B!f^}F)XRhZuD)wlBWX*$#ShyQo zODK9S%QJPCG;F@-BG0I`d3xJ z+`kyAw{unRZN2anjUEx){?)mPS0U356G|uWae7w$1Vy{Cg?_UtdDfO~j^#8_wy-%{ z^7xk_ki_2!Z)SUZ)s-YXN{|GEQ{zqWRja)FuZm7a{yuebZv?Cm9*xpfkv0*AN`+&r z18jbO@$~GQj;o&jS(`G*kSFb(dKuMKvLr(ny@Gqqr@tVgHf(&1UN8~X=Q6u#J|Cmu z+>)>AT~|YG-6`IF&}aB^;TJB)kp7Y2>!Fn0nj&RK@sDw8`b_U3-TFNTh~R2QB=J8o zJzN5X`{FiAsdG=h)93<)+dQ$2ivCm~)DZUs^5sV;v#5EpD7N62j_-9_r;XzGxa-T7FYTYXROc5&)V0e^*hm`)7@!8~ zO~vdP9v{;?V4P4LP9A;uf@S*K>}y9ms%(02(Yqpj6*qafnBvSFt_?pwdb=30y*=S5 zB*F91+up9$H2HjK3xm6UPmRZwX^oksZDa9lbJ(%slI9YBTH12@_($Zi;CB`}>F#mK zl%A;t>+yD`AZL*)5zwq@IoY>e7|okP!(*GCm4;~A7o0r9dBE*gDV#m1tLn3@=Eby1 zM7$b1(D;@vLKO;n<*(*{*b*PoLNM7eF@zfO@O@OExXR%Vu)r1|NoUaiAOYmVivs`NBHMx;n1-R)sC10Z&t0AyT4pkE#S_s<-M8>DLbkJa{j{{~srDv9V5(yftNVp-pYHhVlyS1mG^vbXh&jBnFlMeQh7vMw_>W2VA!>GPR>b&ZQc5Ee6f4>ya;dQ2qOGX zwTGXdx|lww5}m)}c+Jq6TC24l=m_+<-)lzhVTMew!#S>#MNIB!-5b4`ayT$YT~jn! zS!Xh|bMKAunB=6O6Hq^+>HhB5yD%;hT=dQEo$Xwfq>iS9y*gCP-EesZeLEO0ZHb?j z3lFaHn@T7U7XRnk0fzCStX zbdNx>f!&tj54)(GHGNs0ORgaS*pbeK*;Q5@KV+0~LQYbJJ7|Ih?yslZ*8MN;}Cv zn@^IYFjM>V@E%nycUQH!t#3Yz=)i_NL;uk4%Or2q8XV`|H>s+l_v;3xRaYa^ zo+#|@L#&$(%1c`O>e7=W^Uh}~e+KC5_Tb%*NgE1D%$7|Lpq8YOTXz`NxjilG!_|GF zr?-)qMam%8w79)GxyXnNyu%RVznP$TYk)WYr=#0jm?)SVwKoPvn6z{wZUAgu$2F1< zUvxrw!X{7{kFcq@4i?mhVxm%8AERJ$gPFm*Fq_w&*(ffHX`K>CFN zcTaSl@6eaVB_bz-^AY<|aXdR*Nh*6-v?6f`gyMp^4)K3#5!(2MT012fjM4J_Rbfek z&(7+@Q#&WUf{Q}l_z1Jp~ z91@6;SDhOi9}eBAOUDqIhtHMGB|eCVptBk<&a(Z5`BlrCs*Ixt#0YF$PvP3T-%3{T4%;}!U>jl3Tg)QdPMHu_11Jl0fANPL!F0}#14P^H-K!m^=ljq<< zkJ7Q^L~O&1*p=;UfSM55fesm+v7+j4X3BwSXU($c^yC!VRbb-2`rk8T^q%*4a=>ft z{yJpg(Y1Us&N?Mk8?y7_aXWT7~OiXfGw=|7g6mu(yT+ttFgHll1+w(nO=h-sgFN zn0DnNW(g+@d6QdbV`F32i(W;F`ymN4SKO$1=Jl1e{c5>9ueAnuuN>9g`O}6ExBSfS zv&~A4D%X_g>@4eE#Su;&0Uz<!Wqt;uXT!b9jz!ufv&4$Y4RdSk{=wC1&M+KxD zZG^|({DEGc@H(r7?-KO(Y2~q)Y~9|NsEQiB$w4K2x_QrG2o;sIT=%+RbK%B+^V9R` zvawBt+yO2kVS6`K2+47C0pT+h6*E;cB<=lke0>Qu;(55-obFBok43(#&oWKAlo8FC zp7EoO``JJCE{Nk9+1Z5-)fVhyy5)J4&Y2Rn56Ghky)m0;I3wn=&PzJKBwyt2kVHMO z9)hCVd^P}jlSi+vFfv%EFz)to!UOLZN55GBVZZho z>Mb-I-n zIQH65^qmrXZQqRiw~@x*YK;K=B&F36RNDLgxWZf_0xRnoQf zi_924d${fHm)ll;qRb%k3C>WU3xs7@p;7cQEWS&c!6nSCRhhsJ6$tNk@5BOMlk`v< z!V!kC>MX?C;LsyWcB?khwm98i*#_p|_L?QaXecJ;vg=NY4dTr=T zsk&v;m-XZD`?kr8OlBdR_pu$1WAZ%`To?ZWfT}+~N>OuosANuZyVe1-E_Q^{NI`#p zQz}kv*+f9ZI`d3eiMR7p+l&q-U$;wOp31mUp)g zdV3i4fc^p@rCzLw=L>x}iKG|3m*2PjuGCgi+geo#W*yK=wJ2DiaGU$3?Fm%k-`h_< zC-O#7&SlHW>%0rQd%bNMxQMNU+}L2NwtNxzi|QHGOf`9~n`w@&7p<9VX5lfoh?xfv z62JZTvUAv~|MmU!!CSUnZH99+4z9F+H65TgAMgo?HfLWIhQf88k(OH6X}iC-q^nwY z+=CcJb*-I~l6fvg#8W+h!^Q_SIH5`}L}0n)=~jVR29dXnliY5pyFv=US+$s{wr=6q z`gAG-s`y8B{uHSCX+}v7_>S>B?u7BR(vcBLP=~NG_sFrAJE9XuGk<|b?rPT6MV%wS zmbmHO7#q8pL-R@xmDhfKeU*YezZF|F(55bc#_4-7W=~Z%1`O<-0JdbO#jAwEvmrn} zehM9s4i@$zs|u&MnKAxGiYr)n#r!`*5Lf+88U)@84|IR>iK|1)L< zN-WOK5KDyUd5aupF+(Q&m055uJiWARxjr*qx$wS0y0x5RHG`XfY4PF=2!0=@!KV4n z+|;x^hbcLLyJC2ulf#Z|Y%0D5+yQ&!cGP6X1JBSxsspPX^!vJYS+@ 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Ξ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 Hd$IoquqCNB literal 0 HcmV?d00001 diff --git a/metricbeat/docs/modules/azure.asciidoc b/metricbeat/docs/modules/azure.asciidoc index 4db38120041..42d4d619c02 100644 --- a/metricbeat/docs/modules/azure.asciidoc +++ b/metricbeat/docs/modules/azure.asciidoc @@ -45,6 +45,10 @@ The Azure billing dashboards show relevant usage and forecast information: image::./images/metricbeat-azure-billing-overview.png[] +The Azure app_state dashboard shows relevant application insights information: + +image::./images/metricbeat-azure-app-state-overview.png[] + [float] === Module-specific configuration notes @@ -120,6 +124,10 @@ so the `period` for `billing` metricset should be `24h` or multiples of `24h`. === `app_insights` This metricset will collect application insights metrics, the `period` (interval) for the `app-insights` metricset is set by default at `300s`. +[float] +=== `app_state` +This metricset concentrate on the most relevant application insights metrics and provides a dashboard for visualization, the `period` (interval) for the `app_state` metricset is set by default at `300s`. + [float] [[azure-api-cost]] == Additional notes about metrics and costs @@ -242,6 +250,14 @@ metricbeat.modules: api_key: '' metrics: - id: ["requests/count", "requests/duration"] + +- module: azure + metricsets: + - app_state + enabled: true + period: 300s + application_id: '' + api_key: '' ---- [float] @@ -251,6 +267,8 @@ The following metricsets are available: * <> +* <> + * <> * <> @@ -271,6 +289,8 @@ The following metricsets are available: include::azure/app_insights.asciidoc[] +include::azure/app_state.asciidoc[] + include::azure/billing.asciidoc[] include::azure/compute_vm.asciidoc[] diff --git a/metricbeat/docs/modules/azure/app_state.asciidoc b/metricbeat/docs/modules/azure/app_state.asciidoc new file mode 100644 index 00000000000..10485dc818e --- /dev/null +++ b/metricbeat/docs/modules/azure/app_state.asciidoc @@ -0,0 +1,24 @@ +//// +This file is generated! See scripts/mage/docs_collector.go +//// + +[[metricbeat-metricset-azure-app_state]] +[role="xpack"] +=== Azure app_state metricset + +beta[] + +include::../../../../x-pack/metricbeat/module/azure/app_state/_meta/docs.asciidoc[] + + +==== Fields + +For a description of each field in the metricset, see the +<> section. + +Here is an example document generated by this metricset: + +[source,json] +---- +include::../../../../x-pack/metricbeat/module/azure/app_state/_meta/data.json[] +---- diff --git a/metricbeat/docs/modules_list.asciidoc b/metricbeat/docs/modules_list.asciidoc index 2232cf3b070..949657459db 100644 --- a/metricbeat/docs/modules_list.asciidoc +++ b/metricbeat/docs/modules_list.asciidoc @@ -33,7 +33,8 @@ This file is generated! See scripts/mage/docs_collector.go |<> beta[] |<> beta[] |<> |image:./images/icon-yes.png[Prebuilt dashboards are available] | -.10+| .10+| |<> beta[] +.11+| .11+| |<> beta[] +|<> beta[] |<> beta[] |<> |<> diff --git a/x-pack/metricbeat/metricbeat.reference.yml b/x-pack/metricbeat/metricbeat.reference.yml index ff9bffda33e..17c9234ea9f 100644 --- a/x-pack/metricbeat/metricbeat.reference.yml +++ b/x-pack/metricbeat/metricbeat.reference.yml @@ -343,6 +343,14 @@ metricbeat.modules: metrics: - id: ["requests/count", "requests/duration"] +- module: azure + metricsets: + - app_state + enabled: true + period: 300s + application_id: '' + api_key: '' + #--------------------------------- Beat Module --------------------------------- - module: beat metricsets: diff --git a/x-pack/metricbeat/module/azure/_meta/config.reference.yml b/x-pack/metricbeat/module/azure/_meta/config.reference.yml index 1f9ac04529e..b06e466a01f 100644 --- a/x-pack/metricbeat/module/azure/_meta/config.reference.yml +++ b/x-pack/metricbeat/module/azure/_meta/config.reference.yml @@ -102,3 +102,11 @@ api_key: '' metrics: - id: ["requests/count", "requests/duration"] + +- module: azure + metricsets: + - app_state + enabled: true + period: 300s + application_id: '' + api_key: '' diff --git a/x-pack/metricbeat/module/azure/_meta/config.yml b/x-pack/metricbeat/module/azure/_meta/config.yml index 0f497af6fb4..f7215d4f991 100644 --- a/x-pack/metricbeat/module/azure/_meta/config.yml +++ b/x-pack/metricbeat/module/azure/_meta/config.yml @@ -111,3 +111,11 @@ # api_key: '' # metrics: # - id: ["requests/count", "requests/duration"] + +#- module: azure +# metricsets: +# - app_state +# enabled: true +# period: 300s +# application_id: '' +# api_key: '' diff --git a/x-pack/metricbeat/module/azure/_meta/docs.asciidoc b/x-pack/metricbeat/module/azure/_meta/docs.asciidoc index b0f76ecb623..01f6389ab93 100644 --- a/x-pack/metricbeat/module/azure/_meta/docs.asciidoc +++ b/x-pack/metricbeat/module/azure/_meta/docs.asciidoc @@ -37,6 +37,10 @@ The Azure billing dashboards show relevant usage and forecast information: image::./images/metricbeat-azure-billing-overview.png[] +The Azure app_state dashboard shows relevant application insights information: + +image::./images/metricbeat-azure-app-state-overview.png[] + [float] === Module-specific configuration notes @@ -112,6 +116,10 @@ so the `period` for `billing` metricset should be `24h` or multiples of `24h`. === `app_insights` This metricset will collect application insights metrics, the `period` (interval) for the `app-insights` metricset is set by default at `300s`. +[float] +=== `app_state` +This metricset concentrate on the most relevant application insights metrics and provides a dashboard for visualization, the `period` (interval) for the `app_state` metricset is set by default at `300s`. + [float] [[azure-api-cost]] == Additional notes about metrics and costs diff --git a/x-pack/metricbeat/module/azure/_meta/fields.yml b/x-pack/metricbeat/module/azure/_meta/fields.yml index c6471dc108d..a6476fcc957 100644 --- a/x-pack/metricbeat/module/azure/_meta/fields.yml +++ b/x-pack/metricbeat/module/azure/_meta/fields.yml @@ -39,6 +39,10 @@ type: keyword description: > The subscription ID + - name: application_id + type: keyword + description: > + The application ID - name: dimensions.* type: object object_type: keyword diff --git a/x-pack/metricbeat/module/azure/_meta/kibana/7/dashboard/Metricbeat-azure-app-state-overview.json b/x-pack/metricbeat/module/azure/_meta/kibana/7/dashboard/Metricbeat-azure-app-state-overview.json new file mode 100644 index 00000000000..b447a6b276e --- /dev/null +++ b/x-pack/metricbeat/module/azure/_meta/kibana/7/dashboard/Metricbeat-azure-app-state-overview.json @@ -0,0 +1,1509 @@ +{ + "objects": [ + { + "attributes": { + "description": "Provides relevant app insights metrics for web applications", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [ + { + "$state": { + "store": "appState" + }, + "meta": { + "alias": null, + "controlledBy": "1532342651170", + "disabled": false, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "key": "azure.app_state.application_id", + "negate": false, + "params": { + "query": "42cb59a9-d5be-400b-a5c4-69b0a0026ac6" + }, + "type": "phrase" + }, + "query": { + "match_phrase": { + "azure.app_state.application_id": "42cb59a9-d5be-400b-a5c4-69b0a0026ac6" + } + } + } + ], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "optionsJSON": { + "hidePanelTitles": false, + "useMargins": true + }, + "panelsJSON": [ + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 15, + "i": "307a1ecd-284c-4f35-9a3c-d5b77c9a9c82", + "w": 7, + "x": 0, + "y": 0 + }, + "panelIndex": "307a1ecd-284c-4f35-9a3c-d5b77c9a9c82", + "panelRefName": "panel_0", + "version": "7.9.2" + }, + { + "embeddableConfig": { + "title": "Exceptions" + }, + "gridData": { + "h": 15, + "i": "654e745f-360d-4898-89b6-57f788c5f540", + "w": 20, + "x": 7, + "y": 0 + }, + "panelIndex": "654e745f-360d-4898-89b6-57f788c5f540", + "panelRefName": "panel_1", + "title": "Exceptions", + "version": "7.9.2" + }, + { + "embeddableConfig": { + "title": "Available Memory" + }, + "gridData": { + "h": 15, + "i": "5adca737-559d-4b4f-9fa7-58841daa99c5", + "w": 21, + "x": 27, + "y": 0 + }, + "panelIndex": "5adca737-559d-4b4f-9fa7-58841daa99c5", + "panelRefName": "panel_2", + "title": "Available Memory", + "version": "7.9.2" + }, + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 15, + "i": "531cf244-45e0-43c3-9920-8f32397bd973", + "w": 8, + "x": 0, + "y": 15 + }, + "panelIndex": "531cf244-45e0-43c3-9920-8f32397bd973", + "panelRefName": "panel_3", + "version": "7.9.2" + }, + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 15, + "i": "b9242495-babc-48a7-9ad7-56c62b1dc117", + "w": 8, + "x": 8, + "y": 15 + }, + "panelIndex": "b9242495-babc-48a7-9ad7-56c62b1dc117", + "panelRefName": "panel_4", + "version": "7.9.2" + }, + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 15, + "i": "d311025a-f5c5-4e48-9f1c-710f59264c43", + "w": 8, + "x": 16, + "y": 15 + }, + "panelIndex": "d311025a-f5c5-4e48-9f1c-710f59264c43", + "panelRefName": "panel_5", + "version": "7.9.2" + }, + { + "embeddableConfig": { + "title": "Requests" + }, + "gridData": { + "h": 15, + "i": "48974418-b1f7-4050-921e-a83771e125ae", + "w": 24, + "x": 24, + "y": 15 + }, + "panelIndex": "48974418-b1f7-4050-921e-a83771e125ae", + "panelRefName": "panel_6", + "title": "Requests", + "version": "7.9.2" + }, + { + "embeddableConfig": { + "title": "Browser Send/Receive Duration" + }, + "gridData": { + "h": 15, + "i": "39d20db1-316a-4ff3-811a-5571cb4497c3", + "w": 24, + "x": 0, + "y": 30 + }, + "panelIndex": "39d20db1-316a-4ff3-811a-5571cb4497c3", + "panelRefName": "panel_7", + "title": "Browser Send/Receive Duration", + "version": "7.9.2" + }, + { + "embeddableConfig": { + "title": "Browser Networking/Processing Duration" + }, + "gridData": { + "h": 15, + "i": "bc810208-0395-4c70-9057-d7307e064e43", + "w": 24, + "x": 24, + "y": 30 + }, + "panelIndex": "bc810208-0395-4c70-9057-d7307e064e43", + "panelRefName": "panel_8", + "title": "Browser Networking/Processing Duration", + "version": "7.9.2" + }, + { + "embeddableConfig": { + "title": "Process CPU Usage" + }, + "gridData": { + "h": 15, + "i": "ecf6fbfa-ba65-481e-af85-07fd9d5feb5f", + "w": 24, + "x": 0, + "y": 45 + }, + "panelIndex": "ecf6fbfa-ba65-481e-af85-07fd9d5feb5f", + "panelRefName": "panel_9", + "title": "Process CPU Usage", + "version": "7.9.2" + }, + { + "embeddableConfig": { + "title": "Process Private Bytes" + }, + "gridData": { + "h": 15, + "i": "40a1b80b-cd62-446d-91aa-a971bb3769e7", + "w": 24, + "x": 24, + "y": 45 + }, + "panelIndex": "40a1b80b-cd62-446d-91aa-a971bb3769e7", + "panelRefName": "panel_10", + "title": "Process Private Bytes", + "version": "7.9.2" + } + ], + "timeRestore": false, + "title": "[Metricbeat Azure] App State Overview", + "version": 1 + }, + "id": "d5fbd610-03d9-11eb-8034-63f2039e9d3f", + "migrationVersion": { + "dashboard": "7.3.0" + }, + "namespaces": [ + "default" + ], + "references": [ + { + "id": "metricbeat-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + }, + { + "id": "2e5183a0-03da-11eb-8034-63f2039e9d3f", + "name": "panel_0", + "type": "visualization" + }, + { + "id": "1064f9a0-04a5-11eb-8034-63f2039e9d3f", + "name": "panel_1", + "type": "lens" + }, + { + "id": "76cc1d70-04a7-11eb-8034-63f2039e9d3f", + "name": "panel_2", + "type": "lens" + }, + { + "id": "a89c8fd0-03ec-11eb-8034-63f2039e9d3f", + "name": "panel_3", + "type": "lens" + }, + { + "id": "cb5ec410-03ed-11eb-8034-63f2039e9d3f", + "name": "panel_4", + "type": "lens" + }, + { + "id": "0df175c0-03ee-11eb-8034-63f2039e9d3f", + "name": "panel_5", + "type": "lens" + }, + { + "id": "f0678020-04a2-11eb-8034-63f2039e9d3f", + "name": "panel_6", + "type": "lens" + }, + { + "id": "e2704140-04a3-11eb-8034-63f2039e9d3f", + "name": "panel_7", + "type": "lens" + }, + { + "id": "0e74dee0-04a4-11eb-8034-63f2039e9d3f", + "name": "panel_8", + "type": "lens" + }, + { + "id": "cfa361a0-04a8-11eb-8034-63f2039e9d3f", + "name": "panel_9", + "type": "lens" + }, + { + "id": "2b54b2c0-04a8-11eb-8034-63f2039e9d3f", + "name": "panel_10", + "type": "lens" + } + ], + "type": "dashboard", + "updated_at": "2020-10-02T12:22:06.090Z", + "version": "WzMzMDEsMl0=" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [ + { + "$state": { + "store": "appState" + }, + "meta": { + "alias": null, + "controlledBy": "1532342651170", + "disabled": false, + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "key": "azure.application_id", + "negate": false, + "params": { + "query": "42cb59a9-d5be-400b-a5c4-69b0a0026ac6" + }, + "type": "phrase" + }, + "query": { + "match_phrase": { + "azure.application_id": "42cb59a9-d5be-400b-a5c4-69b0a0026ac6" + } + } + } + ], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "App State Filters [Metricbeat Azure]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "controls": [ + { + "fieldName": "azure.application_id", + "id": "1532342651170", + "indexPatternRefName": "control_0_index_pattern", + "label": "Application ID", + "options": { + "multiselect": true, + "order": "desc", + "size": 10, + "type": "terms" + }, + "parent": "", + "type": "list" + }, + { + "fieldName": "azure.dimensions.request_url_host", + "id": "1601559750853", + "indexPatternRefName": "control_1_index_pattern", + "label": "Host URL", + "options": { + "dynamicOptions": true, + "multiselect": true, + "order": "desc", + "size": 5, + "type": "terms" + }, + "parent": "1532342651170", + "type": "list" + }, + { + "fieldName": "azure.dimensions.cloud_role_name", + "id": "1601640368472", + "indexPatternRefName": "control_2_index_pattern", + "label": "Name", + "options": { + "dynamicOptions": true, + "multiselect": true, + "order": "desc", + "size": 5, + "type": "terms" + }, + "parent": "1532342651170", + "type": "list" + }, + { + "fieldName": "azure.dimensions.browser_timing_url_host", + "id": "1601640439434", + "indexPatternRefName": "control_3_index_pattern", + "label": "Browser URL Host", + "options": { + "dynamicOptions": true, + "multiselect": true, + "order": "desc", + "size": 5, + "type": "terms" + }, + "parent": "1532342651170", + "type": "list" + } + ], + "pinFilters": false, + "updateFiltersOnChange": true, + "useTimeFilter": false + }, + "title": "App State Filters [Metricbeat Azure]", + "type": "input_control_vis" + } + }, + "id": "2e5183a0-03da-11eb-8034-63f2039e9d3f", + "migrationVersion": { + "visualization": "7.8.0" + }, + "namespaces": [ + "default" + ], + "references": [ + { + "id": "metricbeat-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + }, + { + "id": "metricbeat-*", + "name": "control_0_index_pattern", + "type": "index-pattern" + }, + { + "id": "metricbeat-*", + "name": "control_1_index_pattern", + "type": "index-pattern" + }, + { + "id": "metricbeat-*", + "name": "control_2_index_pattern", + "type": "index-pattern" + }, + { + "id": "metricbeat-*", + "name": "control_3_index_pattern", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2020-10-02T12:22:51.232Z", + "version": "WzMzMTIsMl0=" + }, + { + "attributes": { + "description": "", + "expression": "kibana\n| kibana_context query=\"{\\\"query\\\":\\\"\\\",\\\"language\\\":\\\"kuery\\\"}\" filters=\"[]\"\n| lens_merge_tables layerIds=\"85644d0a-8011-45af-a751-7961b8bdd071\" \n tables={esaggs index=\"metricbeat-*\" metricsAtAllLevels=true partialRows=true includeFormatHints=true timeFields=\"@timestamp\" aggConfigs=\"[{\\\"id\\\":\\\"bcbccc16-d042-40fa-a9b2-0f09268281ff\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"date_histogram\\\",\\\"schema\\\":\\\"segment\\\",\\\"params\\\":{\\\"field\\\":\\\"@timestamp\\\",\\\"useNormalizedEsInterval\\\":true,\\\"interval\\\":\\\"auto\\\",\\\"drop_partials\\\":false,\\\"min_doc_count\\\":0,\\\"extended_bounds\\\":{}}},{\\\"id\\\":\\\"5788331a-267d-426a-a68e-94a5310af644\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"terms\\\",\\\"schema\\\":\\\"segment\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.dimensions.exception_type\\\",\\\"orderBy\\\":\\\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\",\\\"order\\\":\\\"desc\\\",\\\"size\\\":3,\\\"otherBucket\\\":false,\\\"otherBucketLabel\\\":\\\"Other\\\",\\\"missingBucket\\\":false,\\\"missingBucketLabel\\\":\\\"Missing\\\"}},{\\\"id\\\":\\\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"avg\\\",\\\"schema\\\":\\\"metric\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.app_state.exceptions_count.sum\\\",\\\"missing\\\":0}},{\\\"id\\\":\\\"e5c93c50-bb0a-4609-a7ce-7003f2f9a20e\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"avg\\\",\\\"schema\\\":\\\"metric\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.app_state.exceptions_server.sum\\\",\\\"missing\\\":0}},{\\\"id\\\":\\\"9e183a5e-3dba-4929-b07e-2a3321f7926b\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"avg\\\",\\\"schema\\\":\\\"metric\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.app_state.exceptions_browser.sum\\\",\\\"missing\\\":0}}]\" | lens_rename_columns idMap=\"{\\\"col-0-bcbccc16-d042-40fa-a9b2-0f09268281ff\\\":{\\\"label\\\":\\\"@timestamp\\\",\\\"dataType\\\":\\\"date\\\",\\\"operationType\\\":\\\"date_histogram\\\",\\\"suggestedPriority\\\":1,\\\"sourceField\\\":\\\"@timestamp\\\",\\\"isBucketed\\\":true,\\\"scale\\\":\\\"interval\\\",\\\"params\\\":{\\\"interval\\\":\\\"auto\\\"},\\\"id\\\":\\\"bcbccc16-d042-40fa-a9b2-0f09268281ff\\\"},\\\"col-4-5788331a-267d-426a-a68e-94a5310af644\\\":{\\\"label\\\":\\\"Type\\\",\\\"dataType\\\":\\\"string\\\",\\\"operationType\\\":\\\"terms\\\",\\\"scale\\\":\\\"ordinal\\\",\\\"sourceField\\\":\\\"azure.dimensions.exception_type\\\",\\\"isBucketed\\\":true,\\\"params\\\":{\\\"size\\\":3,\\\"orderBy\\\":{\\\"type\\\":\\\"column\\\",\\\"columnId\\\":\\\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\"},\\\"orderDirection\\\":\\\"desc\\\"},\\\"customLabel\\\":true,\\\"id\\\":\\\"5788331a-267d-426a-a68e-94a5310af644\\\"},\\\"col-5-b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\":{\\\"label\\\":\\\"Total\\\",\\\"dataType\\\":\\\"number\\\",\\\"operationType\\\":\\\"avg\\\",\\\"sourceField\\\":\\\"azure.app_state.exceptions_count.sum\\\",\\\"isBucketed\\\":false,\\\"scale\\\":\\\"ratio\\\",\\\"customLabel\\\":true,\\\"id\\\":\\\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\"},\\\"col-6-e5c93c50-bb0a-4609-a7ce-7003f2f9a20e\\\":{\\\"label\\\":\\\"Server \\\",\\\"dataType\\\":\\\"number\\\",\\\"operationType\\\":\\\"avg\\\",\\\"sourceField\\\":\\\"azure.app_state.exceptions_server.sum\\\",\\\"isBucketed\\\":false,\\\"scale\\\":\\\"ratio\\\",\\\"customLabel\\\":true,\\\"id\\\":\\\"e5c93c50-bb0a-4609-a7ce-7003f2f9a20e\\\"},\\\"col-7-9e183a5e-3dba-4929-b07e-2a3321f7926b\\\":{\\\"label\\\":\\\"Browser\\\",\\\"dataType\\\":\\\"number\\\",\\\"operationType\\\":\\\"avg\\\",\\\"sourceField\\\":\\\"azure.app_state.exceptions_browser.sum\\\",\\\"isBucketed\\\":false,\\\"scale\\\":\\\"ratio\\\",\\\"customLabel\\\":true,\\\"id\\\":\\\"9e183a5e-3dba-4929-b07e-2a3321f7926b\\\"}}\"}\n| lens_xy_chart xTitle=\"@timestamp\" yTitle=\"Total\" legend={lens_xy_legendConfig isVisible=true position=\"right\"} fittingFunction=\"None\" \n layers={lens_xy_layer layerId=\"85644d0a-8011-45af-a751-7961b8bdd071\" hide=false xAccessor=\"bcbccc16-d042-40fa-a9b2-0f09268281ff\" yScaleType=\"linear\" xScaleType=\"time\" isHistogram=true splitAccessor=\"5788331a-267d-426a-a68e-94a5310af644\" seriesType=\"area_stacked\" accessors=\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\" accessors=\"e5c93c50-bb0a-4609-a7ce-7003f2f9a20e\" accessors=\"9e183a5e-3dba-4929-b07e-2a3321f7926b\" columnToLabel=\"{\\\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\":\\\"Total\\\",\\\"e5c93c50-bb0a-4609-a7ce-7003f2f9a20e\\\":\\\"Server \\\",\\\"9e183a5e-3dba-4929-b07e-2a3321f7926b\\\":\\\"Browser\\\",\\\"5788331a-267d-426a-a68e-94a5310af644\\\":\\\"Type\\\"}\"}", + "state": { + "datasourceMetaData": { + "filterableIndexPatterns": [ + { + "id": "metricbeat-*", + "title": "metricbeat-*" + } + ] + }, + "datasourceStates": { + "indexpattern": { + "currentIndexPatternId": "metricbeat-*", + "layers": { + "85644d0a-8011-45af-a751-7961b8bdd071": { + "columnOrder": [ + "bcbccc16-d042-40fa-a9b2-0f09268281ff", + "5788331a-267d-426a-a68e-94a5310af644", + "b0d8f2d4-91f3-469c-8bcb-962a9fb48fba", + "e5c93c50-bb0a-4609-a7ce-7003f2f9a20e", + "9e183a5e-3dba-4929-b07e-2a3321f7926b" + ], + "columns": { + "5788331a-267d-426a-a68e-94a5310af644": { + "customLabel": true, + "dataType": "string", + "isBucketed": true, + "label": "Type", + "operationType": "terms", + "params": { + "orderBy": { + "columnId": "b0d8f2d4-91f3-469c-8bcb-962a9fb48fba", + "type": "column" + }, + "orderDirection": "desc", + "size": 3 + }, + "scale": "ordinal", + "sourceField": "azure.dimensions.exception_type" + }, + "9e183a5e-3dba-4929-b07e-2a3321f7926b": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Browser", + "operationType": "avg", + "scale": "ratio", + "sourceField": "azure.app_state.exceptions_browser.sum" + }, + "b0d8f2d4-91f3-469c-8bcb-962a9fb48fba": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Total", + "operationType": "avg", + "scale": "ratio", + "sourceField": "azure.app_state.exceptions_count.sum" + }, + "bcbccc16-d042-40fa-a9b2-0f09268281ff": { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": { + "interval": "auto" + }, + "scale": "interval", + "sourceField": "@timestamp", + "suggestedPriority": 1 + }, + "e5c93c50-bb0a-4609-a7ce-7003f2f9a20e": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Server ", + "operationType": "avg", + "scale": "ratio", + "sourceField": "azure.app_state.exceptions_server.sum" + } + }, + "indexPatternId": "metricbeat-*" + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "fittingFunction": "None", + "layers": [ + { + "accessors": [ + "b0d8f2d4-91f3-469c-8bcb-962a9fb48fba", + "e5c93c50-bb0a-4609-a7ce-7003f2f9a20e", + "9e183a5e-3dba-4929-b07e-2a3321f7926b" + ], + "layerId": "85644d0a-8011-45af-a751-7961b8bdd071", + "position": "top", + "seriesType": "area_stacked", + "showGridlines": false, + "splitAccessor": "5788331a-267d-426a-a68e-94a5310af644", + "xAccessor": "bcbccc16-d042-40fa-a9b2-0f09268281ff" + } + ], + "legend": { + "isVisible": true, + "position": "right" + }, + "preferredSeriesType": "area_stacked" + } + }, + "title": "App state Exceptions [Metricbeat Azure]", + "visualizationType": "lnsXY" + }, + "id": "1064f9a0-04a5-11eb-8034-63f2039e9d3f", + "migrationVersion": { + "lens": "7.8.0" + }, + "namespaces": [ + "default" + ], + "references": [], + "type": "lens", + "updated_at": "2020-10-02T11:53:16.483Z", + "version": "WzI5NjMsMl0=" + }, + { + "attributes": { + "description": "", + "expression": "kibana\n| kibana_context query=\"{\\\"query\\\":\\\"\\\",\\\"language\\\":\\\"kuery\\\"}\" filters=\"[]\"\n| lens_merge_tables layerIds=\"85644d0a-8011-45af-a751-7961b8bdd071\" \n tables={esaggs index=\"metricbeat-*\" metricsAtAllLevels=true partialRows=true includeFormatHints=true timeFields=\"@timestamp\" aggConfigs=\"[{\\\"id\\\":\\\"bcbccc16-d042-40fa-a9b2-0f09268281ff\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"date_histogram\\\",\\\"schema\\\":\\\"segment\\\",\\\"params\\\":{\\\"field\\\":\\\"@timestamp\\\",\\\"useNormalizedEsInterval\\\":true,\\\"interval\\\":\\\"auto\\\",\\\"drop_partials\\\":false,\\\"min_doc_count\\\":0,\\\"extended_bounds\\\":{}}},{\\\"id\\\":\\\"a1f669d0-c9f2-4bc5-9bdd-e40badd261b9\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"terms\\\",\\\"schema\\\":\\\"segment\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.dimensions.cloud_role_instance\\\",\\\"orderBy\\\":\\\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\",\\\"order\\\":\\\"desc\\\",\\\"size\\\":3,\\\"otherBucket\\\":false,\\\"otherBucketLabel\\\":\\\"Other\\\",\\\"missingBucket\\\":false,\\\"missingBucketLabel\\\":\\\"Missing\\\"}},{\\\"id\\\":\\\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"avg\\\",\\\"schema\\\":\\\"metric\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.app_state.performance_counters_memory_available_bytes.avg\\\",\\\"missing\\\":0}}]\" | lens_rename_columns idMap=\"{\\\"col-0-bcbccc16-d042-40fa-a9b2-0f09268281ff\\\":{\\\"label\\\":\\\"@timestamp\\\",\\\"dataType\\\":\\\"date\\\",\\\"operationType\\\":\\\"date_histogram\\\",\\\"suggestedPriority\\\":1,\\\"sourceField\\\":\\\"@timestamp\\\",\\\"isBucketed\\\":true,\\\"scale\\\":\\\"interval\\\",\\\"params\\\":{\\\"interval\\\":\\\"auto\\\"},\\\"id\\\":\\\"bcbccc16-d042-40fa-a9b2-0f09268281ff\\\"},\\\"col-2-a1f669d0-c9f2-4bc5-9bdd-e40badd261b9\\\":{\\\"label\\\":\\\"Instance\\\",\\\"dataType\\\":\\\"string\\\",\\\"operationType\\\":\\\"terms\\\",\\\"scale\\\":\\\"ordinal\\\",\\\"sourceField\\\":\\\"azure.dimensions.cloud_role_instance\\\",\\\"isBucketed\\\":true,\\\"params\\\":{\\\"size\\\":3,\\\"orderBy\\\":{\\\"type\\\":\\\"column\\\",\\\"columnId\\\":\\\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\"},\\\"orderDirection\\\":\\\"desc\\\"},\\\"customLabel\\\":true,\\\"id\\\":\\\"a1f669d0-c9f2-4bc5-9bdd-e40badd261b9\\\"},\\\"col-3-b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\":{\\\"label\\\":\\\"Available memory\\\",\\\"dataType\\\":\\\"number\\\",\\\"operationType\\\":\\\"avg\\\",\\\"sourceField\\\":\\\"azure.app_state.performance_counters_memory_available_bytes.avg\\\",\\\"isBucketed\\\":false,\\\"scale\\\":\\\"ratio\\\",\\\"customLabel\\\":true,\\\"params\\\":{\\\"format\\\":{\\\"id\\\":\\\"bytes\\\",\\\"params\\\":{\\\"decimals\\\":2}}},\\\"id\\\":\\\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\"}}\" | lens_format_column format=\"bytes\" columnId=\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\" decimals=2}\n| lens_xy_chart xTitle=\"@timestamp\" yTitle=\"Available memory\" legend={lens_xy_legendConfig isVisible=true position=\"right\"} fittingFunction=\"None\" \n layers={lens_xy_layer layerId=\"85644d0a-8011-45af-a751-7961b8bdd071\" hide=false xAccessor=\"bcbccc16-d042-40fa-a9b2-0f09268281ff\" yScaleType=\"linear\" xScaleType=\"time\" isHistogram=true splitAccessor=\"a1f669d0-c9f2-4bc5-9bdd-e40badd261b9\" seriesType=\"area\" accessors=\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\" columnToLabel=\"{\\\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\":\\\"Available memory\\\",\\\"a1f669d0-c9f2-4bc5-9bdd-e40badd261b9\\\":\\\"Instance\\\"}\"}", + "state": { + "datasourceMetaData": { + "filterableIndexPatterns": [ + { + "id": "metricbeat-*", + "title": "metricbeat-*" + } + ] + }, + "datasourceStates": { + "indexpattern": { + "currentIndexPatternId": "metricbeat-*", + "layers": { + "85644d0a-8011-45af-a751-7961b8bdd071": { + "columnOrder": [ + "bcbccc16-d042-40fa-a9b2-0f09268281ff", + "a1f669d0-c9f2-4bc5-9bdd-e40badd261b9", + "b0d8f2d4-91f3-469c-8bcb-962a9fb48fba" + ], + "columns": { + "a1f669d0-c9f2-4bc5-9bdd-e40badd261b9": { + "customLabel": true, + "dataType": "string", + "isBucketed": true, + "label": "Instance", + "operationType": "terms", + "params": { + "orderBy": { + "columnId": "b0d8f2d4-91f3-469c-8bcb-962a9fb48fba", + "type": "column" + }, + "orderDirection": "desc", + "size": 3 + }, + "scale": "ordinal", + "sourceField": "azure.dimensions.cloud_role_instance" + }, + "b0d8f2d4-91f3-469c-8bcb-962a9fb48fba": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Available memory", + "operationType": "avg", + "params": { + "format": { + "id": "bytes", + "params": { + "decimals": 2 + } + } + }, + "scale": "ratio", + "sourceField": "azure.app_state.performance_counters_memory_available_bytes.avg" + }, + "bcbccc16-d042-40fa-a9b2-0f09268281ff": { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": { + "interval": "auto" + }, + "scale": "interval", + "sourceField": "@timestamp", + "suggestedPriority": 1 + } + }, + "indexPatternId": "metricbeat-*" + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "fittingFunction": "None", + "layers": [ + { + "accessors": [ + "b0d8f2d4-91f3-469c-8bcb-962a9fb48fba" + ], + "layerId": "85644d0a-8011-45af-a751-7961b8bdd071", + "position": "top", + "seriesType": "area", + "showGridlines": false, + "splitAccessor": "a1f669d0-c9f2-4bc5-9bdd-e40badd261b9", + "xAccessor": "bcbccc16-d042-40fa-a9b2-0f09268281ff" + } + ], + "legend": { + "isVisible": true, + "position": "right" + }, + "preferredSeriesType": "area" + } + }, + "title": "App state Memory [Metricbeat Azure]", + "visualizationType": "lnsXY" + }, + "id": "76cc1d70-04a7-11eb-8034-63f2039e9d3f", + "migrationVersion": { + "lens": "7.8.0" + }, + "namespaces": [ + "default" + ], + "references": [], + "type": "lens", + "updated_at": "2020-10-02T12:04:46.406Z", + "version": "WzMwNTcsMl0=" + }, + { + "attributes": { + "description": "", + "expression": "kibana\n| kibana_context query=\"{\\\"query\\\":\\\"\\\",\\\"language\\\":\\\"kuery\\\"}\" filters=\"[]\"\n| lens_merge_tables layerIds=\"82e648a8-6d9a-4ae0-9449-b802ce1ac723\" \n tables={esaggs index=\"metricbeat-*\" metricsAtAllLevels=true partialRows=true includeFormatHints=true aggConfigs=\"[{\\\"id\\\":\\\"d62f1bf0-71b4-41ba-9d9c-dc9f4e478ac1\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"avg\\\",\\\"schema\\\":\\\"metric\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.app_state.users_count.unique\\\",\\\"missing\\\":0}}]\" | lens_rename_columns idMap=\"{\\\"col-0-d62f1bf0-71b4-41ba-9d9c-dc9f4e478ac1\\\":{\\\"label\\\":\\\"Unique users\\\",\\\"dataType\\\":\\\"number\\\",\\\"operationType\\\":\\\"avg\\\",\\\"sourceField\\\":\\\"azure.app_state.users_count.unique\\\",\\\"isBucketed\\\":false,\\\"scale\\\":\\\"ratio\\\",\\\"customLabel\\\":true,\\\"id\\\":\\\"d62f1bf0-71b4-41ba-9d9c-dc9f4e478ac1\\\"}}\"}\n| lens_metric_chart title=\"Unique users\" accessor=\"d62f1bf0-71b4-41ba-9d9c-dc9f4e478ac1\" mode=\"full\"", + "state": { + "datasourceMetaData": { + "filterableIndexPatterns": [ + { + "id": "metricbeat-*", + "title": "metricbeat-*" + } + ] + }, + "datasourceStates": { + "indexpattern": { + "currentIndexPatternId": "metricbeat-*", + "layers": { + "82e648a8-6d9a-4ae0-9449-b802ce1ac723": { + "columnOrder": [ + "d62f1bf0-71b4-41ba-9d9c-dc9f4e478ac1" + ], + "columns": { + "d62f1bf0-71b4-41ba-9d9c-dc9f4e478ac1": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Unique users", + "operationType": "avg", + "scale": "ratio", + "sourceField": "azure.app_state.users_count.unique" + } + }, + "indexPatternId": "metricbeat-*" + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "accessor": "d62f1bf0-71b4-41ba-9d9c-dc9f4e478ac1", + "layerId": "82e648a8-6d9a-4ae0-9449-b802ce1ac723" + } + }, + "title": "App state Unique users [Metricbeat Azure]", + "visualizationType": "lnsMetric" + }, + "id": "a89c8fd0-03ec-11eb-8034-63f2039e9d3f", + "migrationVersion": { + "lens": "7.8.0" + }, + "namespaces": [ + "default" + ], + "references": [], + "type": "lens", + "updated_at": "2020-10-01T13:47:34.093Z", + "version": "WzEzMzAsMl0=" + }, + { + "attributes": { + "description": "", + "expression": "kibana\n| kibana_context query=\"{\\\"query\\\":\\\"\\\",\\\"language\\\":\\\"kuery\\\"}\" filters=\"[]\"\n| lens_merge_tables layerIds=\"82e648a8-6d9a-4ae0-9449-b802ce1ac723\" \n tables={esaggs index=\"metricbeat-*\" metricsAtAllLevels=true partialRows=true includeFormatHints=true aggConfigs=\"[{\\\"id\\\":\\\"d62f1bf0-71b4-41ba-9d9c-dc9f4e478ac1\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"avg\\\",\\\"schema\\\":\\\"metric\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.app_state.users_authenticated.unique\\\",\\\"missing\\\":0}}]\" | lens_rename_columns idMap=\"{\\\"col-0-d62f1bf0-71b4-41ba-9d9c-dc9f4e478ac1\\\":{\\\"label\\\":\\\"Unique authenticated users\\\",\\\"dataType\\\":\\\"number\\\",\\\"operationType\\\":\\\"avg\\\",\\\"sourceField\\\":\\\"azure.app_state.users_authenticated.unique\\\",\\\"isBucketed\\\":false,\\\"scale\\\":\\\"ratio\\\",\\\"customLabel\\\":true,\\\"id\\\":\\\"d62f1bf0-71b4-41ba-9d9c-dc9f4e478ac1\\\"}}\"}\n| lens_metric_chart title=\"Unique authenticated users\" accessor=\"d62f1bf0-71b4-41ba-9d9c-dc9f4e478ac1\" mode=\"full\"", + "state": { + "datasourceMetaData": { + "filterableIndexPatterns": [ + { + "id": "metricbeat-*", + "title": "metricbeat-*" + } + ] + }, + "datasourceStates": { + "indexpattern": { + "currentIndexPatternId": "metricbeat-*", + "layers": { + "82e648a8-6d9a-4ae0-9449-b802ce1ac723": { + "columnOrder": [ + "d62f1bf0-71b4-41ba-9d9c-dc9f4e478ac1" + ], + "columns": { + "d62f1bf0-71b4-41ba-9d9c-dc9f4e478ac1": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Unique authenticated users", + "operationType": "avg", + "scale": "ratio", + "sourceField": "azure.app_state.users_authenticated.unique" + } + }, + "indexPatternId": "metricbeat-*" + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "accessor": "d62f1bf0-71b4-41ba-9d9c-dc9f4e478ac1", + "layerId": "82e648a8-6d9a-4ae0-9449-b802ce1ac723" + } + }, + "title": "App state Unique authenticated users [Metricbeat Azure]", + "visualizationType": "lnsMetric" + }, + "id": "cb5ec410-03ed-11eb-8034-63f2039e9d3f", + "migrationVersion": { + "lens": "7.8.0" + }, + "namespaces": [ + "default" + ], + "references": [], + "type": "lens", + "updated_at": "2020-10-01T13:55:41.904Z", + "version": "WzE0MjAsMl0=" + }, + { + "attributes": { + "description": "", + "expression": "kibana\n| kibana_context query=\"{\\\"query\\\":\\\"\\\",\\\"language\\\":\\\"kuery\\\"}\" filters=\"[]\"\n| lens_merge_tables layerIds=\"82e648a8-6d9a-4ae0-9449-b802ce1ac723\" \n tables={esaggs index=\"metricbeat-*\" metricsAtAllLevels=true partialRows=true includeFormatHints=true aggConfigs=\"[{\\\"id\\\":\\\"d62f1bf0-71b4-41ba-9d9c-dc9f4e478ac1\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"avg\\\",\\\"schema\\\":\\\"metric\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.app_state.sessions_count.unique\\\",\\\"missing\\\":0}}]\" | lens_rename_columns idMap=\"{\\\"col-0-d62f1bf0-71b4-41ba-9d9c-dc9f4e478ac1\\\":{\\\"label\\\":\\\"Unique sessions\\\",\\\"dataType\\\":\\\"number\\\",\\\"operationType\\\":\\\"avg\\\",\\\"sourceField\\\":\\\"azure.app_state.sessions_count.unique\\\",\\\"isBucketed\\\":false,\\\"scale\\\":\\\"ratio\\\",\\\"customLabel\\\":true,\\\"id\\\":\\\"d62f1bf0-71b4-41ba-9d9c-dc9f4e478ac1\\\"}}\"}\n| lens_metric_chart title=\"Unique sessions\" accessor=\"d62f1bf0-71b4-41ba-9d9c-dc9f4e478ac1\" mode=\"full\"", + "state": { + "datasourceMetaData": { + "filterableIndexPatterns": [ + { + "id": "metricbeat-*", + "title": "metricbeat-*" + } + ] + }, + "datasourceStates": { + "indexpattern": { + "currentIndexPatternId": "metricbeat-*", + "layers": { + "82e648a8-6d9a-4ae0-9449-b802ce1ac723": { + "columnOrder": [ + "d62f1bf0-71b4-41ba-9d9c-dc9f4e478ac1" + ], + "columns": { + "d62f1bf0-71b4-41ba-9d9c-dc9f4e478ac1": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Unique sessions", + "operationType": "avg", + "scale": "ratio", + "sourceField": "azure.app_state.sessions_count.unique" + } + }, + "indexPatternId": "metricbeat-*" + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "accessor": "d62f1bf0-71b4-41ba-9d9c-dc9f4e478ac1", + "layerId": "82e648a8-6d9a-4ae0-9449-b802ce1ac723" + } + }, + "title": "App state Unique sessions [Metricbeat Azure]", + "visualizationType": "lnsMetric" + }, + "id": "0df175c0-03ee-11eb-8034-63f2039e9d3f", + "migrationVersion": { + "lens": "7.8.0" + }, + "namespaces": [ + "default" + ], + "references": [], + "type": "lens", + "updated_at": "2020-10-01T13:57:33.596Z", + "version": "WzE0NjAsMl0=" + }, + { + "attributes": { + "description": "", + "expression": "kibana\n| kibana_context query=\"{\\\"query\\\":\\\"\\\",\\\"language\\\":\\\"kuery\\\"}\" filters=\"[]\"\n| lens_merge_tables layerIds=\"85644d0a-8011-45af-a751-7961b8bdd071\" \n tables={esaggs index=\"metricbeat-*\" metricsAtAllLevels=true partialRows=true includeFormatHints=true timeFields=\"@timestamp\" aggConfigs=\"[{\\\"id\\\":\\\"bcbccc16-d042-40fa-a9b2-0f09268281ff\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"date_histogram\\\",\\\"schema\\\":\\\"segment\\\",\\\"params\\\":{\\\"field\\\":\\\"@timestamp\\\",\\\"useNormalizedEsInterval\\\":true,\\\"interval\\\":\\\"auto\\\",\\\"drop_partials\\\":false,\\\"min_doc_count\\\":0,\\\"extended_bounds\\\":{}}},{\\\"id\\\":\\\"8864c98b-413a-484f-a61d-336a63ef3f13\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"terms\\\",\\\"schema\\\":\\\"segment\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.dimensions.request_url_host\\\",\\\"orderBy\\\":\\\"9ec4d260-e302-46c4-ac09-50ef54627894\\\",\\\"order\\\":\\\"desc\\\",\\\"size\\\":3,\\\"otherBucket\\\":false,\\\"otherBucketLabel\\\":\\\"Other\\\",\\\"missingBucket\\\":false,\\\"missingBucketLabel\\\":\\\"Missing\\\"}},{\\\"id\\\":\\\"9ec4d260-e302-46c4-ac09-50ef54627894\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"avg\\\",\\\"schema\\\":\\\"metric\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.app_state.requests_count.sum\\\",\\\"missing\\\":0}},{\\\"id\\\":\\\"a47e59dc-fb62-42f8-90e1-236c7c5a073d\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"avg\\\",\\\"schema\\\":\\\"metric\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.app_state.requests_failed.sum\\\",\\\"missing\\\":0}}]\" | lens_rename_columns idMap=\"{\\\"col-0-bcbccc16-d042-40fa-a9b2-0f09268281ff\\\":{\\\"label\\\":\\\"@timestamp\\\",\\\"dataType\\\":\\\"date\\\",\\\"operationType\\\":\\\"date_histogram\\\",\\\"suggestedPriority\\\":1,\\\"sourceField\\\":\\\"@timestamp\\\",\\\"isBucketed\\\":true,\\\"scale\\\":\\\"interval\\\",\\\"params\\\":{\\\"interval\\\":\\\"auto\\\"},\\\"id\\\":\\\"bcbccc16-d042-40fa-a9b2-0f09268281ff\\\"},\\\"col-3-8864c98b-413a-484f-a61d-336a63ef3f13\\\":{\\\"label\\\":\\\"Host URL\\\",\\\"dataType\\\":\\\"string\\\",\\\"operationType\\\":\\\"terms\\\",\\\"scale\\\":\\\"ordinal\\\",\\\"sourceField\\\":\\\"azure.dimensions.request_url_host\\\",\\\"isBucketed\\\":true,\\\"params\\\":{\\\"size\\\":3,\\\"orderBy\\\":{\\\"type\\\":\\\"column\\\",\\\"columnId\\\":\\\"9ec4d260-e302-46c4-ac09-50ef54627894\\\"},\\\"orderDirection\\\":\\\"desc\\\"},\\\"customLabel\\\":true,\\\"id\\\":\\\"8864c98b-413a-484f-a61d-336a63ef3f13\\\"},\\\"col-4-9ec4d260-e302-46c4-ac09-50ef54627894\\\":{\\\"label\\\":\\\"Total requests\\\",\\\"dataType\\\":\\\"number\\\",\\\"operationType\\\":\\\"avg\\\",\\\"sourceField\\\":\\\"azure.app_state.requests_count.sum\\\",\\\"isBucketed\\\":false,\\\"scale\\\":\\\"ratio\\\",\\\"customLabel\\\":true,\\\"id\\\":\\\"9ec4d260-e302-46c4-ac09-50ef54627894\\\"},\\\"col-5-a47e59dc-fb62-42f8-90e1-236c7c5a073d\\\":{\\\"label\\\":\\\"Failed requests\\\",\\\"dataType\\\":\\\"number\\\",\\\"operationType\\\":\\\"avg\\\",\\\"sourceField\\\":\\\"azure.app_state.requests_failed.sum\\\",\\\"isBucketed\\\":false,\\\"scale\\\":\\\"ratio\\\",\\\"customLabel\\\":true,\\\"id\\\":\\\"a47e59dc-fb62-42f8-90e1-236c7c5a073d\\\"}}\"}\n| lens_xy_chart xTitle=\"@timestamp\" yTitle=\"Total requests\" legend={lens_xy_legendConfig isVisible=true position=\"right\"} fittingFunction=\"None\" \n layers={lens_xy_layer layerId=\"85644d0a-8011-45af-a751-7961b8bdd071\" hide=false xAccessor=\"bcbccc16-d042-40fa-a9b2-0f09268281ff\" yScaleType=\"linear\" xScaleType=\"time\" isHistogram=true splitAccessor=\"8864c98b-413a-484f-a61d-336a63ef3f13\" seriesType=\"area\" accessors=\"9ec4d260-e302-46c4-ac09-50ef54627894\" accessors=\"a47e59dc-fb62-42f8-90e1-236c7c5a073d\" columnToLabel=\"{\\\"9ec4d260-e302-46c4-ac09-50ef54627894\\\":\\\"Total requests\\\",\\\"a47e59dc-fb62-42f8-90e1-236c7c5a073d\\\":\\\"Failed requests\\\",\\\"8864c98b-413a-484f-a61d-336a63ef3f13\\\":\\\"Host URL\\\"}\"}", + "state": { + "datasourceMetaData": { + "filterableIndexPatterns": [ + { + "id": "metricbeat-*", + "title": "metricbeat-*" + } + ] + }, + "datasourceStates": { + "indexpattern": { + "currentIndexPatternId": "metricbeat-*", + "layers": { + "85644d0a-8011-45af-a751-7961b8bdd071": { + "columnOrder": [ + "bcbccc16-d042-40fa-a9b2-0f09268281ff", + "8864c98b-413a-484f-a61d-336a63ef3f13", + "9ec4d260-e302-46c4-ac09-50ef54627894", + "a47e59dc-fb62-42f8-90e1-236c7c5a073d" + ], + "columns": { + "8864c98b-413a-484f-a61d-336a63ef3f13": { + "customLabel": true, + "dataType": "string", + "isBucketed": true, + "label": "Host URL", + "operationType": "terms", + "params": { + "orderBy": { + "columnId": "9ec4d260-e302-46c4-ac09-50ef54627894", + "type": "column" + }, + "orderDirection": "desc", + "size": 3 + }, + "scale": "ordinal", + "sourceField": "azure.dimensions.request_url_host" + }, + "9ec4d260-e302-46c4-ac09-50ef54627894": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Total requests", + "operationType": "avg", + "scale": "ratio", + "sourceField": "azure.app_state.requests_count.sum" + }, + "a47e59dc-fb62-42f8-90e1-236c7c5a073d": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Failed requests", + "operationType": "avg", + "scale": "ratio", + "sourceField": "azure.app_state.requests_failed.sum" + }, + "bcbccc16-d042-40fa-a9b2-0f09268281ff": { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": { + "interval": "auto" + }, + "scale": "interval", + "sourceField": "@timestamp", + "suggestedPriority": 1 + } + }, + "indexPatternId": "metricbeat-*" + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "fittingFunction": "None", + "layers": [ + { + "accessors": [ + "9ec4d260-e302-46c4-ac09-50ef54627894", + "a47e59dc-fb62-42f8-90e1-236c7c5a073d" + ], + "layerId": "85644d0a-8011-45af-a751-7961b8bdd071", + "position": "top", + "seriesType": "area", + "showGridlines": false, + "splitAccessor": "8864c98b-413a-484f-a61d-336a63ef3f13", + "xAccessor": "bcbccc16-d042-40fa-a9b2-0f09268281ff" + } + ], + "legend": { + "isVisible": true, + "position": "right" + }, + "preferredSeriesType": "area" + } + }, + "title": "App state Requests [Metricbeat Azure]", + "visualizationType": "lnsXY" + }, + "id": "f0678020-04a2-11eb-8034-63f2039e9d3f", + "migrationVersion": { + "lens": "7.8.0" + }, + "namespaces": [ + "default" + ], + "references": [], + "type": "lens", + "updated_at": "2020-10-02T11:32:22.946Z", + "version": "WzI0MzUsMl0=" + }, + { + "attributes": { + "description": "", + "expression": "kibana\n| kibana_context query=\"{\\\"query\\\":\\\"\\\",\\\"language\\\":\\\"kuery\\\"}\" filters=\"[]\"\n| lens_merge_tables layerIds=\"85644d0a-8011-45af-a751-7961b8bdd071\" \n tables={esaggs index=\"metricbeat-*\" metricsAtAllLevels=true partialRows=true includeFormatHints=true timeFields=\"@timestamp\" aggConfigs=\"[{\\\"id\\\":\\\"bcbccc16-d042-40fa-a9b2-0f09268281ff\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"date_histogram\\\",\\\"schema\\\":\\\"segment\\\",\\\"params\\\":{\\\"field\\\":\\\"@timestamp\\\",\\\"useNormalizedEsInterval\\\":true,\\\"interval\\\":\\\"auto\\\",\\\"drop_partials\\\":false,\\\"min_doc_count\\\":0,\\\"extended_bounds\\\":{}}},{\\\"id\\\":\\\"4d4c068a-0194-4d54-a1fa-3863c3df9331\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"terms\\\",\\\"schema\\\":\\\"segment\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.dimensions.browser_timing_url_path\\\",\\\"orderBy\\\":\\\"be6a3d8b-9428-480b-a7b3-071127726093\\\",\\\"order\\\":\\\"desc\\\",\\\"size\\\":3,\\\"otherBucket\\\":false,\\\"otherBucketLabel\\\":\\\"Other\\\",\\\"missingBucket\\\":false,\\\"missingBucketLabel\\\":\\\"Missing\\\"}},{\\\"id\\\":\\\"be6a3d8b-9428-480b-a7b3-071127726093\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"avg\\\",\\\"schema\\\":\\\"metric\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.app_state.browser_timings_send_duration.avg\\\",\\\"missing\\\":0}},{\\\"id\\\":\\\"6bc1fd35-168d-42d5-b9c8-7078896d8ce4\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"avg\\\",\\\"schema\\\":\\\"metric\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.app_state.browser_timings_total_duration.avg\\\",\\\"missing\\\":0}},{\\\"id\\\":\\\"988e9976-3471-478c-89f6-11fd46458d7f\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"avg\\\",\\\"schema\\\":\\\"metric\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.app_state.browser_timings_receive_duration.avg\\\",\\\"missing\\\":0}}]\" | lens_rename_columns idMap=\"{\\\"col-0-bcbccc16-d042-40fa-a9b2-0f09268281ff\\\":{\\\"label\\\":\\\"@timestamp\\\",\\\"dataType\\\":\\\"date\\\",\\\"operationType\\\":\\\"date_histogram\\\",\\\"suggestedPriority\\\":1,\\\"sourceField\\\":\\\"@timestamp\\\",\\\"isBucketed\\\":true,\\\"scale\\\":\\\"interval\\\",\\\"params\\\":{\\\"interval\\\":\\\"auto\\\"},\\\"id\\\":\\\"bcbccc16-d042-40fa-a9b2-0f09268281ff\\\"},\\\"col-4-4d4c068a-0194-4d54-a1fa-3863c3df9331\\\":{\\\"label\\\":\\\"Url Path\\\",\\\"dataType\\\":\\\"string\\\",\\\"operationType\\\":\\\"terms\\\",\\\"scale\\\":\\\"ordinal\\\",\\\"sourceField\\\":\\\"azure.dimensions.browser_timing_url_path\\\",\\\"isBucketed\\\":true,\\\"params\\\":{\\\"size\\\":3,\\\"orderBy\\\":{\\\"type\\\":\\\"column\\\",\\\"columnId\\\":\\\"be6a3d8b-9428-480b-a7b3-071127726093\\\"},\\\"orderDirection\\\":\\\"desc\\\"},\\\"customLabel\\\":true,\\\"id\\\":\\\"4d4c068a-0194-4d54-a1fa-3863c3df9331\\\"},\\\"col-5-be6a3d8b-9428-480b-a7b3-071127726093\\\":{\\\"label\\\":\\\"Send duration\\\",\\\"dataType\\\":\\\"number\\\",\\\"operationType\\\":\\\"avg\\\",\\\"sourceField\\\":\\\"azure.app_state.browser_timings_send_duration.avg\\\",\\\"isBucketed\\\":false,\\\"scale\\\":\\\"ratio\\\",\\\"params\\\":{},\\\"customLabel\\\":true,\\\"id\\\":\\\"be6a3d8b-9428-480b-a7b3-071127726093\\\"},\\\"col-6-6bc1fd35-168d-42d5-b9c8-7078896d8ce4\\\":{\\\"label\\\":\\\"Total duration\\\",\\\"dataType\\\":\\\"number\\\",\\\"operationType\\\":\\\"avg\\\",\\\"sourceField\\\":\\\"azure.app_state.browser_timings_total_duration.avg\\\",\\\"isBucketed\\\":false,\\\"scale\\\":\\\"ratio\\\",\\\"customLabel\\\":true,\\\"id\\\":\\\"6bc1fd35-168d-42d5-b9c8-7078896d8ce4\\\"},\\\"col-7-988e9976-3471-478c-89f6-11fd46458d7f\\\":{\\\"label\\\":\\\"Receive duration\\\",\\\"dataType\\\":\\\"number\\\",\\\"operationType\\\":\\\"avg\\\",\\\"sourceField\\\":\\\"azure.app_state.browser_timings_receive_duration.avg\\\",\\\"isBucketed\\\":false,\\\"scale\\\":\\\"ratio\\\",\\\"customLabel\\\":true,\\\"id\\\":\\\"988e9976-3471-478c-89f6-11fd46458d7f\\\"}}\"}\n| lens_xy_chart xTitle=\"@timestamp\" yTitle=\"Send duration\" legend={lens_xy_legendConfig isVisible=true position=\"right\"} fittingFunction=\"None\" \n layers={lens_xy_layer layerId=\"85644d0a-8011-45af-a751-7961b8bdd071\" hide=false xAccessor=\"bcbccc16-d042-40fa-a9b2-0f09268281ff\" yScaleType=\"linear\" xScaleType=\"time\" isHistogram=true splitAccessor=\"4d4c068a-0194-4d54-a1fa-3863c3df9331\" seriesType=\"bar\" accessors=\"be6a3d8b-9428-480b-a7b3-071127726093\" accessors=\"6bc1fd35-168d-42d5-b9c8-7078896d8ce4\" accessors=\"988e9976-3471-478c-89f6-11fd46458d7f\" columnToLabel=\"{\\\"be6a3d8b-9428-480b-a7b3-071127726093\\\":\\\"Send duration\\\",\\\"6bc1fd35-168d-42d5-b9c8-7078896d8ce4\\\":\\\"Total duration\\\",\\\"988e9976-3471-478c-89f6-11fd46458d7f\\\":\\\"Receive duration\\\",\\\"4d4c068a-0194-4d54-a1fa-3863c3df9331\\\":\\\"Url Path\\\"}\"}", + "state": { + "datasourceMetaData": { + "filterableIndexPatterns": [ + { + "id": "metricbeat-*", + "title": "metricbeat-*" + } + ] + }, + "datasourceStates": { + "indexpattern": { + "currentIndexPatternId": "metricbeat-*", + "layers": { + "85644d0a-8011-45af-a751-7961b8bdd071": { + "columnOrder": [ + "bcbccc16-d042-40fa-a9b2-0f09268281ff", + "4d4c068a-0194-4d54-a1fa-3863c3df9331", + "be6a3d8b-9428-480b-a7b3-071127726093", + "6bc1fd35-168d-42d5-b9c8-7078896d8ce4", + "988e9976-3471-478c-89f6-11fd46458d7f" + ], + "columns": { + "4d4c068a-0194-4d54-a1fa-3863c3df9331": { + "customLabel": true, + "dataType": "string", + "isBucketed": true, + "label": "Url Path", + "operationType": "terms", + "params": { + "orderBy": { + "columnId": "be6a3d8b-9428-480b-a7b3-071127726093", + "type": "column" + }, + "orderDirection": "desc", + "size": 3 + }, + "scale": "ordinal", + "sourceField": "azure.dimensions.browser_timing_url_path" + }, + "6bc1fd35-168d-42d5-b9c8-7078896d8ce4": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Total duration", + "operationType": "avg", + "scale": "ratio", + "sourceField": "azure.app_state.browser_timings_total_duration.avg" + }, + "988e9976-3471-478c-89f6-11fd46458d7f": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Receive duration", + "operationType": "avg", + "scale": "ratio", + "sourceField": "azure.app_state.browser_timings_receive_duration.avg" + }, + "bcbccc16-d042-40fa-a9b2-0f09268281ff": { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": { + "interval": "auto" + }, + "scale": "interval", + "sourceField": "@timestamp", + "suggestedPriority": 1 + }, + "be6a3d8b-9428-480b-a7b3-071127726093": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Send duration", + "operationType": "avg", + "params": {}, + "scale": "ratio", + "sourceField": "azure.app_state.browser_timings_send_duration.avg" + } + }, + "indexPatternId": "metricbeat-*" + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "fittingFunction": "None", + "layers": [ + { + "accessors": [ + "be6a3d8b-9428-480b-a7b3-071127726093", + "6bc1fd35-168d-42d5-b9c8-7078896d8ce4", + "988e9976-3471-478c-89f6-11fd46458d7f" + ], + "layerId": "85644d0a-8011-45af-a751-7961b8bdd071", + "position": "top", + "seriesType": "bar", + "showGridlines": false, + "splitAccessor": "4d4c068a-0194-4d54-a1fa-3863c3df9331", + "xAccessor": "bcbccc16-d042-40fa-a9b2-0f09268281ff" + } + ], + "legend": { + "isVisible": true, + "position": "right" + }, + "preferredSeriesType": "bar" + } + }, + "title": "App state Browser Send/Receive Duration [Metricbeat Azure]", + "visualizationType": "lnsXY" + }, + "id": "e2704140-04a3-11eb-8034-63f2039e9d3f", + "migrationVersion": { + "lens": "7.8.0" + }, + "namespaces": [ + "default" + ], + "references": [], + "type": "lens", + "updated_at": "2020-10-02T11:39:09.012Z", + "version": "WzI2MzQsMl0=" + }, + { + "attributes": { + "description": "", + "expression": "kibana\n| kibana_context query=\"{\\\"query\\\":\\\"\\\",\\\"language\\\":\\\"kuery\\\"}\" filters=\"[]\"\n| lens_merge_tables layerIds=\"85644d0a-8011-45af-a751-7961b8bdd071\" \n tables={esaggs index=\"metricbeat-*\" metricsAtAllLevels=true partialRows=true includeFormatHints=true timeFields=\"@timestamp\" aggConfigs=\"[{\\\"id\\\":\\\"bcbccc16-d042-40fa-a9b2-0f09268281ff\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"date_histogram\\\",\\\"schema\\\":\\\"segment\\\",\\\"params\\\":{\\\"field\\\":\\\"@timestamp\\\",\\\"useNormalizedEsInterval\\\":true,\\\"interval\\\":\\\"auto\\\",\\\"drop_partials\\\":false,\\\"min_doc_count\\\":0,\\\"extended_bounds\\\":{}}},{\\\"id\\\":\\\"4d4c068a-0194-4d54-a1fa-3863c3df9331\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"terms\\\",\\\"schema\\\":\\\"segment\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.dimensions.browser_timing_url_path\\\",\\\"orderBy\\\":\\\"_key\\\",\\\"order\\\":\\\"asc\\\",\\\"size\\\":3,\\\"otherBucket\\\":false,\\\"otherBucketLabel\\\":\\\"Other\\\",\\\"missingBucket\\\":false,\\\"missingBucketLabel\\\":\\\"Missing\\\"}},{\\\"id\\\":\\\"b5a75764-e98b-434b-a0f0-5658a4aa1cf6\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"avg\\\",\\\"schema\\\":\\\"metric\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.app_state.browser_timings_network_duration.avg\\\",\\\"missing\\\":0}},{\\\"id\\\":\\\"ab158cba-532f-47f8-8450-db883504dc0f\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"avg\\\",\\\"schema\\\":\\\"metric\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.app_state.browser_timings_processing_duration.avg\\\",\\\"missing\\\":0}}]\" | lens_rename_columns idMap=\"{\\\"col-0-bcbccc16-d042-40fa-a9b2-0f09268281ff\\\":{\\\"label\\\":\\\"@timestamp\\\",\\\"dataType\\\":\\\"date\\\",\\\"operationType\\\":\\\"date_histogram\\\",\\\"suggestedPriority\\\":1,\\\"sourceField\\\":\\\"@timestamp\\\",\\\"isBucketed\\\":true,\\\"scale\\\":\\\"interval\\\",\\\"params\\\":{\\\"interval\\\":\\\"auto\\\"},\\\"id\\\":\\\"bcbccc16-d042-40fa-a9b2-0f09268281ff\\\"},\\\"col-3-4d4c068a-0194-4d54-a1fa-3863c3df9331\\\":{\\\"label\\\":\\\"Url Path\\\",\\\"dataType\\\":\\\"string\\\",\\\"operationType\\\":\\\"terms\\\",\\\"scale\\\":\\\"ordinal\\\",\\\"sourceField\\\":\\\"azure.dimensions.browser_timing_url_path\\\",\\\"isBucketed\\\":true,\\\"params\\\":{\\\"size\\\":3,\\\"orderBy\\\":{\\\"type\\\":\\\"alphabetical\\\"},\\\"orderDirection\\\":\\\"asc\\\"},\\\"customLabel\\\":true,\\\"id\\\":\\\"4d4c068a-0194-4d54-a1fa-3863c3df9331\\\"},\\\"col-4-b5a75764-e98b-434b-a0f0-5658a4aa1cf6\\\":{\\\"label\\\":\\\"Networking duration\\\",\\\"dataType\\\":\\\"number\\\",\\\"operationType\\\":\\\"avg\\\",\\\"sourceField\\\":\\\"azure.app_state.browser_timings_network_duration.avg\\\",\\\"isBucketed\\\":false,\\\"scale\\\":\\\"ratio\\\",\\\"customLabel\\\":true,\\\"id\\\":\\\"b5a75764-e98b-434b-a0f0-5658a4aa1cf6\\\"},\\\"col-5-ab158cba-532f-47f8-8450-db883504dc0f\\\":{\\\"label\\\":\\\"Processing duration\\\",\\\"dataType\\\":\\\"number\\\",\\\"operationType\\\":\\\"avg\\\",\\\"sourceField\\\":\\\"azure.app_state.browser_timings_processing_duration.avg\\\",\\\"isBucketed\\\":false,\\\"scale\\\":\\\"ratio\\\",\\\"customLabel\\\":true,\\\"id\\\":\\\"ab158cba-532f-47f8-8450-db883504dc0f\\\"}}\"}\n| lens_xy_chart xTitle=\"@timestamp\" yTitle=\"Networking duration\" legend={lens_xy_legendConfig isVisible=true position=\"right\"} fittingFunction=\"None\" \n layers={lens_xy_layer layerId=\"85644d0a-8011-45af-a751-7961b8bdd071\" hide=false xAccessor=\"bcbccc16-d042-40fa-a9b2-0f09268281ff\" yScaleType=\"linear\" xScaleType=\"time\" isHistogram=true splitAccessor=\"4d4c068a-0194-4d54-a1fa-3863c3df9331\" seriesType=\"bar\" accessors=\"b5a75764-e98b-434b-a0f0-5658a4aa1cf6\" accessors=\"ab158cba-532f-47f8-8450-db883504dc0f\" columnToLabel=\"{\\\"b5a75764-e98b-434b-a0f0-5658a4aa1cf6\\\":\\\"Networking duration\\\",\\\"ab158cba-532f-47f8-8450-db883504dc0f\\\":\\\"Processing duration\\\",\\\"4d4c068a-0194-4d54-a1fa-3863c3df9331\\\":\\\"Url Path\\\"}\"}", + "state": { + "datasourceMetaData": { + "filterableIndexPatterns": [ + { + "id": "metricbeat-*", + "title": "metricbeat-*" + } + ] + }, + "datasourceStates": { + "indexpattern": { + "currentIndexPatternId": "metricbeat-*", + "layers": { + "85644d0a-8011-45af-a751-7961b8bdd071": { + "columnOrder": [ + "bcbccc16-d042-40fa-a9b2-0f09268281ff", + "4d4c068a-0194-4d54-a1fa-3863c3df9331", + "b5a75764-e98b-434b-a0f0-5658a4aa1cf6", + "ab158cba-532f-47f8-8450-db883504dc0f" + ], + "columns": { + "4d4c068a-0194-4d54-a1fa-3863c3df9331": { + "customLabel": true, + "dataType": "string", + "isBucketed": true, + "label": "Url Path", + "operationType": "terms", + "params": { + "orderBy": { + "type": "alphabetical" + }, + "orderDirection": "asc", + "size": 3 + }, + "scale": "ordinal", + "sourceField": "azure.dimensions.browser_timing_url_path" + }, + "ab158cba-532f-47f8-8450-db883504dc0f": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Processing duration", + "operationType": "avg", + "scale": "ratio", + "sourceField": "azure.app_state.browser_timings_processing_duration.avg" + }, + "b5a75764-e98b-434b-a0f0-5658a4aa1cf6": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Networking duration", + "operationType": "avg", + "scale": "ratio", + "sourceField": "azure.app_state.browser_timings_network_duration.avg" + }, + "bcbccc16-d042-40fa-a9b2-0f09268281ff": { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": { + "interval": "auto" + }, + "scale": "interval", + "sourceField": "@timestamp", + "suggestedPriority": 1 + } + }, + "indexPatternId": "metricbeat-*" + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "fittingFunction": "None", + "layers": [ + { + "accessors": [ + "b5a75764-e98b-434b-a0f0-5658a4aa1cf6", + "ab158cba-532f-47f8-8450-db883504dc0f" + ], + "layerId": "85644d0a-8011-45af-a751-7961b8bdd071", + "position": "top", + "seriesType": "bar", + "showGridlines": false, + "splitAccessor": "4d4c068a-0194-4d54-a1fa-3863c3df9331", + "xAccessor": "bcbccc16-d042-40fa-a9b2-0f09268281ff" + } + ], + "legend": { + "isVisible": true, + "position": "right" + }, + "preferredSeriesType": "bar" + } + }, + "title": "App state Browser Networking/Processing Duration [Metricbeat Azure]", + "visualizationType": "lnsXY" + }, + "id": "0e74dee0-04a4-11eb-8034-63f2039e9d3f", + "migrationVersion": { + "lens": "7.8.0" + }, + "namespaces": [ + "default" + ], + "references": [], + "type": "lens", + "updated_at": "2020-10-02T11:40:22.862Z", + "version": "WzI2ODQsMl0=" + }, + { + "attributes": { + "description": "", + "expression": "kibana\n| kibana_context query=\"{\\\"query\\\":\\\"\\\",\\\"language\\\":\\\"kuery\\\"}\" filters=\"[]\"\n| lens_merge_tables layerIds=\"85644d0a-8011-45af-a751-7961b8bdd071\" \n tables={esaggs index=\"metricbeat-*\" metricsAtAllLevels=true partialRows=true includeFormatHints=true timeFields=\"@timestamp\" aggConfigs=\"[{\\\"id\\\":\\\"bcbccc16-d042-40fa-a9b2-0f09268281ff\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"date_histogram\\\",\\\"schema\\\":\\\"segment\\\",\\\"params\\\":{\\\"field\\\":\\\"@timestamp\\\",\\\"useNormalizedEsInterval\\\":true,\\\"interval\\\":\\\"auto\\\",\\\"drop_partials\\\":false,\\\"min_doc_count\\\":0,\\\"extended_bounds\\\":{}}},{\\\"id\\\":\\\"a1f669d0-c9f2-4bc5-9bdd-e40badd261b9\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"terms\\\",\\\"schema\\\":\\\"segment\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.dimensions.cloud_role_instance\\\",\\\"orderBy\\\":\\\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\",\\\"order\\\":\\\"desc\\\",\\\"size\\\":3,\\\"otherBucket\\\":false,\\\"otherBucketLabel\\\":\\\"Other\\\",\\\"missingBucket\\\":false,\\\"missingBucketLabel\\\":\\\"Missing\\\"}},{\\\"id\\\":\\\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"avg\\\",\\\"schema\\\":\\\"metric\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.app_state.performance_counters_process_cpu_percentage_total.avg\\\",\\\"missing\\\":0}},{\\\"id\\\":\\\"252dfd5f-26bd-4861-bb01-4b3530cadd95\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"avg\\\",\\\"schema\\\":\\\"metric\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.app_state.performance_counters_process_cpu_percentage.avg\\\",\\\"missing\\\":0}}]\" | lens_rename_columns idMap=\"{\\\"col-0-bcbccc16-d042-40fa-a9b2-0f09268281ff\\\":{\\\"label\\\":\\\"@timestamp\\\",\\\"dataType\\\":\\\"date\\\",\\\"operationType\\\":\\\"date_histogram\\\",\\\"suggestedPriority\\\":1,\\\"sourceField\\\":\\\"@timestamp\\\",\\\"isBucketed\\\":true,\\\"scale\\\":\\\"interval\\\",\\\"params\\\":{\\\"interval\\\":\\\"auto\\\"},\\\"id\\\":\\\"bcbccc16-d042-40fa-a9b2-0f09268281ff\\\"},\\\"col-3-a1f669d0-c9f2-4bc5-9bdd-e40badd261b9\\\":{\\\"label\\\":\\\"Instance\\\",\\\"dataType\\\":\\\"string\\\",\\\"operationType\\\":\\\"terms\\\",\\\"scale\\\":\\\"ordinal\\\",\\\"sourceField\\\":\\\"azure.dimensions.cloud_role_instance\\\",\\\"isBucketed\\\":true,\\\"params\\\":{\\\"size\\\":3,\\\"orderBy\\\":{\\\"type\\\":\\\"column\\\",\\\"columnId\\\":\\\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\"},\\\"orderDirection\\\":\\\"desc\\\"},\\\"customLabel\\\":true,\\\"id\\\":\\\"a1f669d0-c9f2-4bc5-9bdd-e40badd261b9\\\"},\\\"col-4-b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\":{\\\"label\\\":\\\"Total CPU percentage \\\",\\\"dataType\\\":\\\"number\\\",\\\"operationType\\\":\\\"avg\\\",\\\"sourceField\\\":\\\"azure.app_state.performance_counters_process_cpu_percentage_total.avg\\\",\\\"isBucketed\\\":false,\\\"scale\\\":\\\"ratio\\\",\\\"customLabel\\\":true,\\\"params\\\":{\\\"format\\\":{\\\"id\\\":\\\"percent\\\",\\\"params\\\":{\\\"decimals\\\":2}}},\\\"id\\\":\\\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\"},\\\"col-5-252dfd5f-26bd-4861-bb01-4b3530cadd95\\\":{\\\"label\\\":\\\"CPU percentage\\\",\\\"dataType\\\":\\\"number\\\",\\\"operationType\\\":\\\"avg\\\",\\\"sourceField\\\":\\\"azure.app_state.performance_counters_process_cpu_percentage.avg\\\",\\\"isBucketed\\\":false,\\\"scale\\\":\\\"ratio\\\",\\\"params\\\":{\\\"format\\\":{\\\"id\\\":\\\"percent\\\",\\\"params\\\":{\\\"decimals\\\":2}}},\\\"customLabel\\\":true,\\\"id\\\":\\\"252dfd5f-26bd-4861-bb01-4b3530cadd95\\\"}}\" | lens_format_column format=\"percent\" columnId=\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\" decimals=2 | lens_format_column format=\"percent\" columnId=\"252dfd5f-26bd-4861-bb01-4b3530cadd95\" decimals=2}\n| lens_xy_chart xTitle=\"@timestamp\" yTitle=\"Total CPU percentage \" legend={lens_xy_legendConfig isVisible=true position=\"right\"} fittingFunction=\"None\" \n layers={lens_xy_layer layerId=\"85644d0a-8011-45af-a751-7961b8bdd071\" hide=false xAccessor=\"bcbccc16-d042-40fa-a9b2-0f09268281ff\" yScaleType=\"linear\" xScaleType=\"time\" isHistogram=true splitAccessor=\"a1f669d0-c9f2-4bc5-9bdd-e40badd261b9\" seriesType=\"area\" accessors=\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\" accessors=\"252dfd5f-26bd-4861-bb01-4b3530cadd95\" columnToLabel=\"{\\\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\":\\\"Total CPU percentage \\\",\\\"252dfd5f-26bd-4861-bb01-4b3530cadd95\\\":\\\"CPU percentage\\\",\\\"a1f669d0-c9f2-4bc5-9bdd-e40badd261b9\\\":\\\"Instance\\\"}\"}", + "state": { + "datasourceMetaData": { + "filterableIndexPatterns": [ + { + "id": "metricbeat-*", + "title": "metricbeat-*" + } + ] + }, + "datasourceStates": { + "indexpattern": { + "currentIndexPatternId": "metricbeat-*", + "layers": { + "85644d0a-8011-45af-a751-7961b8bdd071": { + "columnOrder": [ + "bcbccc16-d042-40fa-a9b2-0f09268281ff", + "a1f669d0-c9f2-4bc5-9bdd-e40badd261b9", + "b0d8f2d4-91f3-469c-8bcb-962a9fb48fba", + "252dfd5f-26bd-4861-bb01-4b3530cadd95" + ], + "columns": { + "252dfd5f-26bd-4861-bb01-4b3530cadd95": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "CPU percentage", + "operationType": "avg", + "params": { + "format": { + "id": "percent", + "params": { + "decimals": 2 + } + } + }, + "scale": "ratio", + "sourceField": "azure.app_state.performance_counters_process_cpu_percentage.avg" + }, + "a1f669d0-c9f2-4bc5-9bdd-e40badd261b9": { + "customLabel": true, + "dataType": "string", + "isBucketed": true, + "label": "Instance", + "operationType": "terms", + "params": { + "orderBy": { + "columnId": "b0d8f2d4-91f3-469c-8bcb-962a9fb48fba", + "type": "column" + }, + "orderDirection": "desc", + "size": 3 + }, + "scale": "ordinal", + "sourceField": "azure.dimensions.cloud_role_instance" + }, + "b0d8f2d4-91f3-469c-8bcb-962a9fb48fba": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Total CPU percentage ", + "operationType": "avg", + "params": { + "format": { + "id": "percent", + "params": { + "decimals": 2 + } + } + }, + "scale": "ratio", + "sourceField": "azure.app_state.performance_counters_process_cpu_percentage_total.avg" + }, + "bcbccc16-d042-40fa-a9b2-0f09268281ff": { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": { + "interval": "auto" + }, + "scale": "interval", + "sourceField": "@timestamp", + "suggestedPriority": 1 + } + }, + "indexPatternId": "metricbeat-*" + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "fittingFunction": "None", + "layers": [ + { + "accessors": [ + "b0d8f2d4-91f3-469c-8bcb-962a9fb48fba", + "252dfd5f-26bd-4861-bb01-4b3530cadd95" + ], + "layerId": "85644d0a-8011-45af-a751-7961b8bdd071", + "position": "top", + "seriesType": "area", + "showGridlines": false, + "splitAccessor": "a1f669d0-c9f2-4bc5-9bdd-e40badd261b9", + "xAccessor": "bcbccc16-d042-40fa-a9b2-0f09268281ff" + } + ], + "legend": { + "isVisible": true, + "position": "right" + }, + "preferredSeriesType": "area" + } + }, + "title": "App state Process CPU usage [Metricbeat Azure]", + "visualizationType": "lnsXY" + }, + "id": "cfa361a0-04a8-11eb-8034-63f2039e9d3f", + "migrationVersion": { + "lens": "7.8.0" + }, + "namespaces": [ + "default" + ], + "references": [], + "type": "lens", + "updated_at": "2020-10-02T12:14:24.954Z", + "version": "WzMyNTMsMl0=" + }, + { + "attributes": { + "description": "", + "expression": "kibana\n| kibana_context query=\"{\\\"query\\\":\\\"\\\",\\\"language\\\":\\\"kuery\\\"}\" filters=\"[]\"\n| lens_merge_tables layerIds=\"85644d0a-8011-45af-a751-7961b8bdd071\" \n tables={esaggs index=\"metricbeat-*\" metricsAtAllLevels=true partialRows=true includeFormatHints=true timeFields=\"@timestamp\" aggConfigs=\"[{\\\"id\\\":\\\"bcbccc16-d042-40fa-a9b2-0f09268281ff\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"date_histogram\\\",\\\"schema\\\":\\\"segment\\\",\\\"params\\\":{\\\"field\\\":\\\"@timestamp\\\",\\\"useNormalizedEsInterval\\\":true,\\\"interval\\\":\\\"auto\\\",\\\"drop_partials\\\":false,\\\"min_doc_count\\\":0,\\\"extended_bounds\\\":{}}},{\\\"id\\\":\\\"a1f669d0-c9f2-4bc5-9bdd-e40badd261b9\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"terms\\\",\\\"schema\\\":\\\"segment\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.dimensions.cloud_role_instance\\\",\\\"orderBy\\\":\\\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\",\\\"order\\\":\\\"desc\\\",\\\"size\\\":3,\\\"otherBucket\\\":false,\\\"otherBucketLabel\\\":\\\"Other\\\",\\\"missingBucket\\\":false,\\\"missingBucketLabel\\\":\\\"Missing\\\"}},{\\\"id\\\":\\\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\",\\\"enabled\\\":true,\\\"type\\\":\\\"avg\\\",\\\"schema\\\":\\\"metric\\\",\\\"params\\\":{\\\"field\\\":\\\"azure.app_state.performance_counters_process_private_bytes.avg\\\",\\\"missing\\\":0}}]\" | lens_rename_columns idMap=\"{\\\"col-0-bcbccc16-d042-40fa-a9b2-0f09268281ff\\\":{\\\"label\\\":\\\"@timestamp\\\",\\\"dataType\\\":\\\"date\\\",\\\"operationType\\\":\\\"date_histogram\\\",\\\"suggestedPriority\\\":1,\\\"sourceField\\\":\\\"@timestamp\\\",\\\"isBucketed\\\":true,\\\"scale\\\":\\\"interval\\\",\\\"params\\\":{\\\"interval\\\":\\\"auto\\\"},\\\"id\\\":\\\"bcbccc16-d042-40fa-a9b2-0f09268281ff\\\"},\\\"col-2-a1f669d0-c9f2-4bc5-9bdd-e40badd261b9\\\":{\\\"label\\\":\\\"Instance\\\",\\\"dataType\\\":\\\"string\\\",\\\"operationType\\\":\\\"terms\\\",\\\"scale\\\":\\\"ordinal\\\",\\\"sourceField\\\":\\\"azure.dimensions.cloud_role_instance\\\",\\\"isBucketed\\\":true,\\\"params\\\":{\\\"size\\\":3,\\\"orderBy\\\":{\\\"type\\\":\\\"column\\\",\\\"columnId\\\":\\\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\"},\\\"orderDirection\\\":\\\"desc\\\"},\\\"customLabel\\\":true,\\\"id\\\":\\\"a1f669d0-c9f2-4bc5-9bdd-e40badd261b9\\\"},\\\"col-3-b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\":{\\\"label\\\":\\\"Process private bytes\\\",\\\"dataType\\\":\\\"number\\\",\\\"operationType\\\":\\\"avg\\\",\\\"sourceField\\\":\\\"azure.app_state.performance_counters_process_private_bytes.avg\\\",\\\"isBucketed\\\":false,\\\"scale\\\":\\\"ratio\\\",\\\"customLabel\\\":true,\\\"params\\\":{\\\"format\\\":{\\\"id\\\":\\\"bytes\\\",\\\"params\\\":{\\\"decimals\\\":2}}},\\\"id\\\":\\\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\"}}\" | lens_format_column format=\"bytes\" columnId=\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\" decimals=2}\n| lens_xy_chart xTitle=\"@timestamp\" yTitle=\"Process private bytes\" legend={lens_xy_legendConfig isVisible=true position=\"right\"} fittingFunction=\"None\" \n layers={lens_xy_layer layerId=\"85644d0a-8011-45af-a751-7961b8bdd071\" hide=false xAccessor=\"bcbccc16-d042-40fa-a9b2-0f09268281ff\" yScaleType=\"linear\" xScaleType=\"time\" isHistogram=true splitAccessor=\"a1f669d0-c9f2-4bc5-9bdd-e40badd261b9\" seriesType=\"area\" accessors=\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\" columnToLabel=\"{\\\"b0d8f2d4-91f3-469c-8bcb-962a9fb48fba\\\":\\\"Process private bytes\\\",\\\"a1f669d0-c9f2-4bc5-9bdd-e40badd261b9\\\":\\\"Instance\\\"}\"}", + "state": { + "datasourceMetaData": { + "filterableIndexPatterns": [ + { + "id": "metricbeat-*", + "title": "metricbeat-*" + } + ] + }, + "datasourceStates": { + "indexpattern": { + "currentIndexPatternId": "metricbeat-*", + "layers": { + "85644d0a-8011-45af-a751-7961b8bdd071": { + "columnOrder": [ + "bcbccc16-d042-40fa-a9b2-0f09268281ff", + "a1f669d0-c9f2-4bc5-9bdd-e40badd261b9", + "b0d8f2d4-91f3-469c-8bcb-962a9fb48fba" + ], + "columns": { + "a1f669d0-c9f2-4bc5-9bdd-e40badd261b9": { + "customLabel": true, + "dataType": "string", + "isBucketed": true, + "label": "Instance", + "operationType": "terms", + "params": { + "orderBy": { + "columnId": "b0d8f2d4-91f3-469c-8bcb-962a9fb48fba", + "type": "column" + }, + "orderDirection": "desc", + "size": 3 + }, + "scale": "ordinal", + "sourceField": "azure.dimensions.cloud_role_instance" + }, + "b0d8f2d4-91f3-469c-8bcb-962a9fb48fba": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Process private bytes", + "operationType": "avg", + "params": { + "format": { + "id": "bytes", + "params": { + "decimals": 2 + } + } + }, + "scale": "ratio", + "sourceField": "azure.app_state.performance_counters_process_private_bytes.avg" + }, + "bcbccc16-d042-40fa-a9b2-0f09268281ff": { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": { + "interval": "auto" + }, + "scale": "interval", + "sourceField": "@timestamp", + "suggestedPriority": 1 + } + }, + "indexPatternId": "metricbeat-*" + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "fittingFunction": "None", + "layers": [ + { + "accessors": [ + "b0d8f2d4-91f3-469c-8bcb-962a9fb48fba" + ], + "layerId": "85644d0a-8011-45af-a751-7961b8bdd071", + "position": "top", + "seriesType": "area", + "showGridlines": false, + "splitAccessor": "a1f669d0-c9f2-4bc5-9bdd-e40badd261b9", + "xAccessor": "bcbccc16-d042-40fa-a9b2-0f09268281ff" + } + ], + "legend": { + "isVisible": true, + "position": "right" + }, + "preferredSeriesType": "area" + } + }, + "title": "App state Process Private Bytes [Metricbeat Azure]", + "visualizationType": "lnsXY" + }, + "id": "2b54b2c0-04a8-11eb-8034-63f2039e9d3f", + "migrationVersion": { + "lens": "7.8.0" + }, + "namespaces": [ + "default" + ], + "references": [], + "type": "lens", + "updated_at": "2020-10-02T12:09:49.291Z", + "version": "WzMxMjMsMl0=" + } + ], + "version": "7.9.2" +} diff --git a/x-pack/metricbeat/module/azure/app_insights/_meta/data.json b/x-pack/metricbeat/module/azure/app_insights/_meta/data.json index 3d1f07c1ac1..c9b35f39923 100644 --- a/x-pack/metricbeat/module/azure/app_insights/_meta/data.json +++ b/x-pack/metricbeat/module/azure/app_insights/_meta/data.json @@ -1,16 +1,15 @@ { "@timestamp": "2017-10-12T08:05:34.853Z", - "azure" : { - "app_insights" : { - "metrics" : { - "requests_failed" : { - "sum" : 182 - }, - "request_name" : "GET /favicon.ico" - }, - "start_date" : "2020-07-12T10:52:11.831Z", - "end_date" : "2020-07-12T12:52:11.831Z", - "application_id" : "42cb59a9-d5be-400b-a5c4-69b0a0026ac6" + "azure": { + "app_insights": { + "end_date": "2020-10-02T13:17:45.691Z", + "start_date": "2020-10-02T13:12:45.691Z" + }, + "application_id": "42cb59a9-d5be-400b-a5c4-69b0a00434fdf4", + "metrics": { + "requests_count": { + "sum": 0 + } } }, "cloud": { diff --git a/x-pack/metricbeat/module/azure/app_insights/_meta/fields.yml b/x-pack/metricbeat/module/azure/app_insights/_meta/fields.yml index 40ab8560827..4e8fa91edbd 100644 --- a/x-pack/metricbeat/module/azure/app_insights/_meta/fields.yml +++ b/x-pack/metricbeat/module/azure/app_insights/_meta/fields.yml @@ -4,10 +4,6 @@ description: > application insights fields: - - name: application_id - type: keyword - description: > - The application ID - name: start_date type: date description: > diff --git a/x-pack/metricbeat/module/azure/app_insights/app_insights.go b/x-pack/metricbeat/module/azure/app_insights/app_insights.go index 8ffe02eb860..091a72f465e 100644 --- a/x-pack/metricbeat/module/azure/app_insights/app_insights.go +++ b/x-pack/metricbeat/module/azure/app_insights/app_insights.go @@ -23,6 +23,7 @@ type Config struct { ApiKey string `config:"api_key" validate:"required"` Period time.Duration `config:"period" validate:"nonzero,required"` Metrics []Metric `config:"metrics" validate:"required"` + Namespace string `config:"namespace"` } // Metric struct used for configuration options @@ -70,7 +71,7 @@ func (m *MetricSet) Fetch(report mb.ReporterV2) error { if err != nil { return errors.Wrap(err, "error retrieving metric values") } - events := EventsMapping(results, m.client.Config.ApplicationId) + events := EventsMapping(results, m.client.Config.ApplicationId, m.client.Config.Namespace) for _, event := range events { isOpen := report.Event(event) if !isOpen { diff --git a/x-pack/metricbeat/module/azure/app_insights/app_insights_integration_test.go b/x-pack/metricbeat/module/azure/app_insights/app_insights_integration_test.go index 3cb93663007..c33f05c2de6 100644 --- a/x-pack/metricbeat/module/azure/app_insights/app_insights_integration_test.go +++ b/x-pack/metricbeat/module/azure/app_insights/app_insights_integration_test.go @@ -10,12 +10,20 @@ package app_insights import ( "testing" + "github.com/elastic/beats/v7/x-pack/metricbeat/module/azure/test" + "github.com/stretchr/testify/assert" mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" ) +var metrics = []map[string]interface{}{{ + "id": "requests/count", +}} + func TestFetchMetricset(t *testing.T) { + config := test.GetConfigForInsights(t, "app_insights") + config["metrics"] = metrics metricSet := mbtest.NewReportingMetricSetV2Error(t, config) events, errs := mbtest.ReportingFetchV2Error(metricSet) if len(errs) > 0 { @@ -26,6 +34,8 @@ func TestFetchMetricset(t *testing.T) { } func TestData(t *testing.T) { + config := test.GetConfigForInsights(t, "app_insights") + config["metrics"] = metrics metricSet := mbtest.NewFetcher(t, config) metricSet.WriteEvents(t, "/") } diff --git a/x-pack/metricbeat/module/azure/app_insights/client.go b/x-pack/metricbeat/module/azure/app_insights/client.go index b78f2257d3f..49794da4d3f 100644 --- a/x-pack/metricbeat/module/azure/app_insights/client.go +++ b/x-pack/metricbeat/module/azure/app_insights/client.go @@ -23,9 +23,6 @@ type Client struct { Log *logp.Logger } -type MetricValue struct { -} - // NewClient instantiates the an Azure monitoring client func NewClient(config Config) (*Client, error) { service, err := NewService(config) diff --git a/x-pack/metricbeat/module/azure/app_insights/data.go b/x-pack/metricbeat/module/azure/app_insights/data.go index df7efdbeaba..1cd661cc3a0 100644 --- a/x-pack/metricbeat/module/azure/app_insights/data.go +++ b/x-pack/metricbeat/module/azure/app_insights/data.go @@ -6,98 +6,217 @@ package app_insights import ( "fmt" + "regexp" "strings" + "github.com/Azure/azure-sdk-for-go/services/preview/appinsights/v1/insights" "github.com/Azure/go-autorest/autorest/date" - "github.com/Azure/azure-sdk-for-go/services/preview/appinsights/v1/insights" + "github.com/elastic/beats/v7/x-pack/metricbeat/module/azure" "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/metricbeat/mb" ) -func EventsMapping(metricValues insights.ListMetricsResultsItem, applicationId string) []mb.Event { - var events []mb.Event - if metricValues.Value == nil { - return events - } - groupedAddProp := make(map[string][]insights.MetricsResultInfo) +const aggsRegex = "_(?:sum|count|unique|avg|min|max)$" + +// segmentNames list is used to filter out the dimension from the api response. Based on the body format it is not possible to detect what was the segment selected +var segmentNames = []string{ + "request_source", "request_name", "request_url_host", "request_url_path", "request_success", "request_result_code", "request_performance_bucket", "operation_name", "operation_synthetic", "operation_synthetic_source", "user_authenticated", "application_version", "client_type", "client_model", + "client_os", "client_city", "client_state_or_province", "client_country_or_region", "client_browser", "cloud_role_name", "cloud_role_instance", "custom_dimensions__ms_processedb_by_metric_extractors", "custom_dimensions_developer_mode", + "page_view_name", "page_view_url_path", "page_view_url_host", "page_view_performance_bucket", "custom_dimensions_ibiza_session_id", "custom_dimensions_part_instance", "browser_timing_name", "browser_timing_url_host", "browser_timing_url_path", "browser_timing_performance_bucket", + "trace_severity_level", "type", "custom_dimensions_agent_session", "custom_dimensions_agent_version", "custom_dimensions_machine_name", "custom_dimensions_running_mode", "custom_dimensions_source", "custom_dimensions_agent_assembly_version", "custom_dimensions_agent_process_session", + "custom_dimensions_hashed_machine_name", + "custom_dimensions_data_cube", "dependency_target", "dependency_type", "dependency_name", "dependency_success", "dependency_result_code", "dependency_performance_bucket", "custom_dimensions_container", "custom_dimensions_blob", "custom_dimensions_error_message", + "custom_event_name", "custom_dimensions_event_name", "custom_dimensions_page_title", "custom_dimensions_service_profiler_content", "custom_dimensions_executing_assembly_file_version", "custom_dimensions_service_profiler_version", "custom_dimensions_process_id", "custom_dimensions_request_id", + "custom_dimensions_running_session", "custom_dimensions_problem_id", "custom_dimensions_snapshot_context", "custom_dimensions_snapshot_version", "custom_dimensions_duration", "custom_dimensions_snapshot_id", "custom_dimensions_stamp_id", "custom_dimensions_de_optimization_id", + "custom_dimensions_method", "custom_dimensions_parent_process_id", "custom_dimensions_section", "custom_dimensions_configuration", "custom_dimensions_dump_folder", "custom_dimensions_reason", "custom_dimensions_extension_version", "custom_dimensions_site_name", + "availability_result_name", "availability_result_location", "availability_result_success", "custom_dimensions_full_test_result_available", "exception_problem_id", "exception_handled_at", "exception_type", "exception_assembly", "exception_method", "custom_dimensions_custom_perf_counter", + "exception_severity_level", "custom_dimensions_url", "custom_dimensions_ai.snapshot_stampid", "custom_dimensions_ai.snapshot_id", "custom_dimensions_ai.snapshot_version", "custom_dimensions_ai.snapshot_planid", "custom_dimensions__ms_example", "custom_dimensions_sa_origin_app_id", + "custom_dimensions_base_sdk_target_framework", "custom_dimensions_runtime_framework", "custom_dimensions__ms_aggregation_interval_ms", "custom_dimensions_problem_id", "custom_dimensions_operation_name", "custom_dimensions_request_success", "custom_dimensions__ms_metric_id", + "custom_dimensions_dependency_success", "custom_dimensions__ms_is_autocollected", "custom_dimensions_dependency_type", "performance_counter_name", "performance_counter_category", "performance_counter_counter", "performance_counter_instance", "custom_dimensions_counter_instance_name", +} + +type MetricValue struct { + SegmentName map[string]string + Value map[string]interface{} + Segments []MetricValue + Interval string + Start *date.Time + End *date.Time +} + +func mapMetricValues(metricValues insights.ListMetricsResultsItem) []MetricValue { + var mapped []MetricValue for _, item := range *metricValues.Value { + metricValue := MetricValue{ + Start: item.Body.Value.Start, + End: item.Body.Value.End, + Value: map[string]interface{}{}, + SegmentName: map[string]string{}, + } + metricValue.Interval = fmt.Sprintf("%sTO%s", item.Body.Value.Start, item.Body.Value.End) if item.Body != nil && item.Body.Value != nil { if item.Body.Value.AdditionalProperties != nil { - groupedAddProp[fmt.Sprintf("%sTO%s", item.Body.Value.Start, item.Body.Value.End)] = - append(groupedAddProp[fmt.Sprintf("%sTO%s", item.Body.Value.Start, item.Body.Value.End)], *item.Body.Value) - } else if item.Body.Value.Segments != nil { - for _, segment := range *item.Body.Value.Segments { - event, ok := createSegmentEvent(*item.Body.Value.Start, *item.Body.Value.End, segment, applicationId) - if ok { - events = append(events, event) + metrics := getAdditionalPropMetric(item.Body.Value.AdditionalProperties) + for key, metric := range metrics { + if isSegment(key) { + metricValue.SegmentName[key] = metric.(string) + } else { + metricValue.Value[key] = metric } } } + if item.Body.Value.Segments != nil { + for _, segment := range *item.Body.Value.Segments { + metVal := mapSegment(segment, metricValue.SegmentName) + metricValue.Segments = append(metricValue.Segments, metVal) + } + } + mapped = append(mapped, metricValue) } + } - if len(groupedAddProp) > 0 { - for _, val := range groupedAddProp { - event, ok := createEvent(val, applicationId) - if ok { - events = append(events, event) + return mapped +} + +func mapSegment(segment insights.MetricsSegmentInfo, parentSeg map[string]string) MetricValue { + metricValue := MetricValue{Value: map[string]interface{}{}, SegmentName: map[string]string{}} + if segment.AdditionalProperties != nil { + metrics := getAdditionalPropMetric(segment.AdditionalProperties) + for key, metric := range metrics { + if isSegment(key) { + metricValue.SegmentName[key] = metric.(string) + } else { + metricValue.Value[key] = metric + } + } + } + if len(parentSeg) > 0 { + for key, val := range parentSeg { + metricValue.SegmentName[key] = val + } + } + if segment.Segments != nil { + for _, segment := range *segment.Segments { + metVal := mapSegment(segment, metricValue.SegmentName) + metricValue.Segments = append(metricValue.Segments, metVal) + } + } + + return metricValue +} + +func isSegment(metric string) bool { + for _, seg := range segmentNames { + if metric == seg { + return true + } + } + return false +} + +func EventsMapping(metricValues insights.ListMetricsResultsItem, applicationId string, namespace string) []mb.Event { + var events []mb.Event + if metricValues.Value == nil { + return events + } + groupedAddProp := make(map[string][]MetricValue) + mValues := mapMetricValues(metricValues) + + var segValues []MetricValue + for _, mv := range mValues { + if len(mv.Segments) == 0 { + groupedAddProp[mv.Interval] = append(groupedAddProp[mv.Interval], mv) + } else { + segValues = append(segValues, mv) + } + } + + for _, val := range groupedAddProp { + event := createNoSegEvent(val, applicationId, namespace) + if len(event.MetricSetFields) > 0 { + events = append(events, event) + } + } + for _, val := range segValues { + for _, seg := range val.Segments { + lastSeg := getValue(seg) + for _, ls := range lastSeg { + events = append(events, createSegEvent(val, ls, applicationId, namespace)) } } } return events } -func createSegmentEvent(start date.Time, end date.Time, segment insights.MetricsSegmentInfo, applicationId string) (mb.Event, bool) { - metricList := common.MapStr{} - metrics := getMetric(segment.AdditionalProperties) - if len(metrics) == 0 { - return mb.Event{}, false +func getValue(metric MetricValue) []MetricValue { + var values []MetricValue + if metric.Segments == nil { + return []MetricValue{metric} + } + for _, met := range metric.Segments { + values = append(values, getValue(met)...) } - for key, metric := range metrics { + return values +} + +func createSegEvent(parentMetricValue MetricValue, metricValue MetricValue, applicationId string, namespace string) mb.Event { + metricList := common.MapStr{} + for key, metric := range metricValue.Value { metricList.Put(key, metric) } + if len(metricList) == 0 { + return mb.Event{} + } + event := createEvent(parentMetricValue.Start, parentMetricValue.End, applicationId, namespace, metricList) + if len(parentMetricValue.SegmentName) > 0 { + event.ModuleFields.Put("dimensions", parentMetricValue.SegmentName) + } + if len(metricValue.SegmentName) > 0 { + event.ModuleFields.Put("dimensions", metricValue.SegmentName) + } + return event +} + +func createEvent(start *date.Time, end *date.Time, applicationId string, namespace string, metricList common.MapStr) mb.Event { event := mb.Event{ - MetricSetFields: common.MapStr{ - "start_date": start, - "end_date": end, + ModuleFields: common.MapStr{ "application_id": applicationId, }, + MetricSetFields: common.MapStr{ + "start_date": start, + "end_date": end, + }, Timestamp: end.Time, } event.RootFields = common.MapStr{} event.RootFields.Put("cloud.provider", "azure") - event.MetricSetFields.Put("metrics", metricList) - return event, true + if namespace == "" { + event.ModuleFields.Put("metrics", metricList) + } else { + for key, metric := range metricList { + event.MetricSetFields.Put(key, metric) + } + } + return event } -func createEvent(values []insights.MetricsResultInfo, applicationId string) (mb.Event, bool) { +func createNoSegEvent(values []MetricValue, applicationId string, namespace string) mb.Event { metricList := common.MapStr{} for _, value := range values { - metrics := getMetric(value.AdditionalProperties) - for key, metric := range metrics { + for key, metric := range value.Value { metricList.Put(key, metric) } } if len(metricList) == 0 { - return mb.Event{}, false + return mb.Event{} } + return createEvent(values[0].Start, values[0].End, applicationId, namespace, metricList) - event := mb.Event{ - MetricSetFields: common.MapStr{ - "start_date": values[0].Start, - "end_date": values[0].End, - "application_id": applicationId, - }, - Timestamp: values[0].End.Time, - } - event.RootFields = common.MapStr{} - event.RootFields.Put("cloud.provider", "azure") - event.MetricSetFields.Put("metrics", metricList) - return event, true } -func getMetric(addProp map[string]interface{}) map[string]interface{} { +func getAdditionalPropMetric(addProp map[string]interface{}) map[string]interface{} { metricNames := make(map[string]interface{}) for key, val := range addProp { switch val.(type) { @@ -115,5 +234,19 @@ func getMetric(addProp map[string]interface{}) map[string]interface{} { } func cleanMetricNames(metric string) string { - return strings.Replace(metric, "/", "_", -1) + metric = strings.Replace(metric, "/", "_", -1) + metric = strings.Replace(metric, " ", "_", -1) + metric = azure.ReplaceUpperCase(metric) + obj := strings.Split(metric, ".") + for index := range obj { + // in some cases a trailing "_" is found + obj[index] = strings.TrimPrefix(obj[index], "_") + obj[index] = strings.TrimSuffix(obj[index], "_") + } + metric = strings.ToLower(strings.Join(obj, "_")) + aggsRegex := regexp.MustCompile(aggsRegex) + metric = aggsRegex.ReplaceAllStringFunc(metric, func(str string) string { + return strings.Replace(str, "_", ".", -1) + }) + return metric } diff --git a/x-pack/metricbeat/module/azure/app_insights/data_test.go b/x-pack/metricbeat/module/azure/app_insights/data_test.go index ebe4e7d98aa..f65f9f84b1d 100644 --- a/x-pack/metricbeat/module/azure/app_insights/data_test.go +++ b/x-pack/metricbeat/module/azure/app_insights/data_test.go @@ -39,20 +39,32 @@ func TestEventMapping(t *testing.T) { Value: &metrics, } applicationId := "abc" - events := EventsMapping(result, applicationId) + events := EventsMapping(result, applicationId, "") assert.Equal(t, len(events), 1) for _, event := range events { val1, _ := event.MetricSetFields.GetValue("start_date") assert.Equal(t, val1, &startDate) val2, _ := event.MetricSetFields.GetValue("end_date") assert.Equal(t, val2, &startDate) - val3, _ := event.MetricSetFields.GetValue("metrics.requests_count") + val3, _ := event.ModuleFields.GetValue("metrics.requests_count") assert.Equal(t, val3, common.MapStr{"sum": 12}) - val5, _ := event.MetricSetFields.GetValue("metrics.requests_failed") + val5, _ := event.ModuleFields.GetValue("metrics.requests_failed") assert.Equal(t, val5, common.MapStr{"sum": 10}) - val4, _ := event.MetricSetFields.GetValue("application_id") + val4, _ := event.ModuleFields.GetValue("application_id") assert.Equal(t, val4, applicationId) } } + +func TestCleanMetricNames(t *testing.T) { + ex := "customDimensions/ExecutingAssemblyFileVersion" + result := cleanMetricNames(ex) + assert.Equal(t, result, "custom_dimensions_executing_assembly_file_version") + ex = "customDimensions/_MS.AggregationIntervalMs" + result = cleanMetricNames(ex) + assert.Equal(t, result, "custom_dimensions__ms_aggregation_interval_ms") + ex = "customDimensions/_MS.IsAutocollected" + result = cleanMetricNames(ex) + assert.Equal(t, result, "custom_dimensions__ms_is_autocollected") +} diff --git a/x-pack/metricbeat/module/azure/app_state/_meta/data.json b/x-pack/metricbeat/module/azure/app_state/_meta/data.json new file mode 100644 index 00000000000..c4744db7f60 --- /dev/null +++ b/x-pack/metricbeat/module/azure/app_state/_meta/data.json @@ -0,0 +1,32 @@ +{ + "@timestamp": "2017-10-12T08:05:34.853Z", + "azure": { + "app_state": { + "end_date": "2020-10-02T13:23:11.221Z", + "requests_count": { + "sum": 16 + }, + "start_date": "2020-10-01T13:23:11.221Z" + }, + "application_id": "42cb59a9-d5be-400b-a5c4-69b0a00434fdf4", + "dimensions": { + "request_name": "GET /auth", + "request_url_host": "demoapplogobs.azurewebsites.net" + } + }, + "cloud": { + "provider": "azure" + }, + "event": { + "dataset": "azure.app_state", + "duration": 115000, + "module": "azure" + }, + "metricset": { + "name": "app_state", + "period": 10000 + }, + "service": { + "type": "azure" + } +} diff --git a/x-pack/metricbeat/module/azure/app_state/_meta/docs.asciidoc b/x-pack/metricbeat/module/azure/app_state/_meta/docs.asciidoc new file mode 100644 index 00000000000..4517ccfed0d --- /dev/null +++ b/x-pack/metricbeat/module/azure/app_state/_meta/docs.asciidoc @@ -0,0 +1,12 @@ +This is the app_state metricset of the module azure. + +This metricset allows users to retrieve application insights metrics from specified applications. + +[float] +==== Config options to identify resources + +`application_id`:: (_[]string_) ID of the application. This is Application ID from the API Access settings blade in the Azure portal. + +`api_key`:: (_[]string_) The API key which will be generated, more on the steps here https://dev.applicationinsights.io/documentation/Authorization/API-key-and-App-ID. + + diff --git a/x-pack/metricbeat/module/azure/app_state/_meta/fields.yml b/x-pack/metricbeat/module/azure/app_state/_meta/fields.yml new file mode 100644 index 00000000000..fad928c090f --- /dev/null +++ b/x-pack/metricbeat/module/azure/app_state/_meta/fields.yml @@ -0,0 +1,86 @@ +- name: app_state + type: group + release: beta + description: > + application state + fields: + - name: start_date + type: date + description: > + The start date + - name: end_date + type: date + description: > + The end date + - name: requests_count.sum + type: float + description: > + Request count + - name: requests_failed.sum + type: float + description: > + Request failed count + - name: users_count.unique + type: float + description: > + User count + - name: sessions_count.unique + type: float + description: > + Session count + - name: users_authenticated.unique + type: float + description: > + Authenticated users count + - name: browser_timings_network_duration.avg + type: float + description: > + Browser timings network duration + - name: browser_timings_send_duration.avg + type: float + description: > + Browser timings send duration + - name: browser_timings_receive_uration.avg + type: float + description: > + Browser timings receive duration + - name: browser_timings_processing_duration.avg + type: float + description: > + Browser timings processing duration + - name: browser_timings_total_duration.avg + type: float + description: > + Browser timings total duration + - name: exceptions_count.sum + type: float + description: > + Exception count + - name: exceptions_browser.sum + type: float + description: > + Exception count at browser level + - name: exceptions_server.sum + type: float + description: > + Exception count at server level + - name: performance_counters_memory_available_bytes.avg + type: float + description: > + Performance counters memory available bytes + - name: performance_counters_process_private_bytes.avg + type: float + description: > + Performance counters process private bytes + - name: performance_counters_process_cpu_percentage_total.avg + type: float + description: > + Performance counters process cpu percentage total + - name: performance_counters_process_cpu_percentage.avg + type: float + description: > + Performance counters process cpu percentage + - name: performance_counters_processiobytes_per_second.avg + type: float + description: > + Performance counters process IO bytes per second diff --git a/x-pack/metricbeat/module/azure/app_state/app_state_integration_test.go b/x-pack/metricbeat/module/azure/app_state/app_state_integration_test.go new file mode 100644 index 00000000000..8762cae440d --- /dev/null +++ b/x-pack/metricbeat/module/azure/app_state/app_state_integration_test.go @@ -0,0 +1,38 @@ +// 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. + +// +build integration +// +build azure + +package app_state + +import ( + "testing" + + "github.com/elastic/beats/v7/x-pack/metricbeat/module/azure/test" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + + // Register input module and metricset + _ "github.com/elastic/beats/v7/x-pack/metricbeat/module/azure/app_insights" +) + +func TestFetchMetricset(t *testing.T) { + config := test.GetConfigForInsights(t, "app_state") + metricSet := mbtest.NewReportingMetricSetV2Error(t, config) + events, errs := mbtest.ReportingFetchV2Error(metricSet) + if len(errs) > 0 { + t.Fatalf("Expected 0 error, had %d. %v\n", len(errs), errs) + } + assert.NotEmpty(t, events) + mbtest.TestMetricsetFieldsDocumented(t, metricSet, events) +} + +func TestData(t *testing.T) { + config := test.GetConfigForInsights(t, "app_state") + metricSet := mbtest.NewFetcher(t, config) + metricSet.WriteEvents(t, "/") +} diff --git a/x-pack/metricbeat/module/azure/app_state/app_state_test.go b/x-pack/metricbeat/module/azure/app_state/app_state_test.go new file mode 100644 index 00000000000..a8dbab88081 --- /dev/null +++ b/x-pack/metricbeat/module/azure/app_state/app_state_test.go @@ -0,0 +1,17 @@ +// 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 app_state + +import ( + "os" + + "github.com/elastic/beats/v7/metricbeat/mb" +) + +func init() { + // To be moved to some kind of helper + os.Setenv("BEAT_STRICT_PERMS", "false") + mb.Registry.SetSecondarySource(mb.NewLightModulesSource("../../../module")) +} diff --git a/x-pack/metricbeat/module/azure/app_state/manifest.yml b/x-pack/metricbeat/module/azure/app_state/manifest.yml new file mode 100644 index 00000000000..5c1d08a2ceb --- /dev/null +++ b/x-pack/metricbeat/module/azure/app_state/manifest.yml @@ -0,0 +1,29 @@ +default: false +input: + module: azure + metricset: app_insights + defaults: + namespace: app_state + metrics: + - id: ["requests/count", "requests/failed"] + segment: ["request/urlHost", "request/name"] + aggregation: ["sum"] + interval: "P5M" + - id: ["users/count", "sessions/count", "users/authenticated"] + segment: ["request/urlHost"] + aggregation: ["unique"] + interval: "P5M" + - id: ["browserTimings/networkDuration", "browserTimings/sendDuration", "browserTimings/receiveDuration", "browserTimings/processingDuration", "browserTimings/totalDuration"] + segment: ["browserTiming/urlHost", "browserTiming/urlPath"] + aggregation: ["avg"] + interval: "P5M" + top: 5 + - id: ["exceptions/count", "exceptions/browser", "exceptions/server"] + segment: ["exception/type", "cloud/roleName"] + aggregation: ["sum"] + interval: "P5M" + - id: ["performanceCounters/memoryAvailableBytes", "performanceCounters/processCpuPercentageTotal", "performanceCounters/processCpuPercentage", "performanceCounters/processIOBytesPerSecond", + "performanceCounters/processPrivateBytes"] + aggregation: ["avg"] + segment: ["cloud/roleName", "cloud/roleInstance"] + interval: "P5M" diff --git a/x-pack/metricbeat/module/azure/data.go b/x-pack/metricbeat/module/azure/data.go index bf77f657416..4d479ef45d5 100644 --- a/x-pack/metricbeat/module/azure/data.go +++ b/x-pack/metricbeat/module/azure/data.go @@ -120,7 +120,7 @@ func managePropertyName(metric string) string { // create an object in case of ":" resultMetricName = strings.Replace(resultMetricName, "_-_", "_", -1) // replace uppercases with underscores - resultMetricName = replaceUpperCase(resultMetricName) + resultMetricName = ReplaceUpperCase(resultMetricName) // avoid cases as this "logicaldisk_avg._disk_sec_per_transfer" obj := strings.Split(resultMetricName, ".") @@ -134,8 +134,8 @@ func managePropertyName(metric string) string { return resultMetricName } -// replaceUpperCase func will replace upper case with '_' -func replaceUpperCase(src string) string { +// ReplaceUpperCase func will replace upper case with '_' +func ReplaceUpperCase(src string) string { replaceUpperCaseRegexp := regexp.MustCompile(replaceUpperCaseRegex) return replaceUpperCaseRegexp.ReplaceAllStringFunc(src, func(str string) string { var newStr string diff --git a/x-pack/metricbeat/module/azure/data_test.go b/x-pack/metricbeat/module/azure/data_test.go index cdfad1965f8..cca7edc0233 100644 --- a/x-pack/metricbeat/module/azure/data_test.go +++ b/x-pack/metricbeat/module/azure/data_test.go @@ -47,10 +47,10 @@ func TestGetDimensionValue(t *testing.T) { } func TestReplaceUpperCase(t *testing.T) { - result := replaceUpperCase("TestReplaceUpper_Case") + result := ReplaceUpperCase("TestReplaceUpper_Case") assert.Equal(t, result, "Test_replace_upper_Case") // should not split on acronyms - result = replaceUpperCase("CPU_Percentage") + result = ReplaceUpperCase("CPU_Percentage") assert.Equal(t, result, "CPU_Percentage") } diff --git a/x-pack/metricbeat/module/azure/fields.go b/x-pack/metricbeat/module/azure/fields.go index 4c0ad95ad98..6a308a5d2ae 100644 --- a/x-pack/metricbeat/module/azure/fields.go +++ b/x-pack/metricbeat/module/azure/fields.go @@ -19,5 +19,5 @@ func init() { // AssetAzure returns asset data. // This is the base64 encoded gzipped contents of module/azure. func AssetAzure() string { - return "eJzUV8tu2zoQ3fsrBl4GSD7AiwsEt5suuuteGJNjhY1EEuQorfv1hR6kKVHyo1aKxIsAEcnzEM8MqUd4peMO8HfjaAPAiivawfa5/X+7AZDkhVOWldE7+G8DAP1cqI1sqnaJo4rQ0w5K3AAcFFXS77qJj6CxphN4++Ojbac609jhyQzDGCaFYlVT6VDpOBIgX+n40ziZPJ8F7n/fXwieexvETokZ3MDoyJvGCcoIUw9X0AUc8JaEOihKpU7tjiwfLY0Glh1fkBGktMvBHIATWbPUU4srcMfXkGNHw1j6p4dZWrP/QYInQ/3D4pywZEpRo7VKl8P87cP2NhN9bKKNTmwWmvavtziTmptjGqHAU0WCk9wENt/sI0Sh5P2cKSB8/ZIRSlWT9sro8T4t7NGF/bl2b85oHpVyIi4T3k/xTw836z5UBhcG/1b1t14MOOLGaZK5XLS2UNqr8oX9xf4T+/CeGK9TgNZWSmC3zTM857pSsnQcObi/R6SykvSl9J7RcSGR51vjzMAVvB1ovjZwkpZrM5KWy3zzYYUbmuE0tLBqK2wNDBqz5O5VVSldvkdoB2hALaHxWBJIYlTVtckVjXOkxXHdzM6iBkrriPFXIYyf7sbyPl3g/H+KFXszWXRck+aifbCuyxM4ZOAns0Y2WS7vJB5A88tQYO2SUHQVvGJ99vm60Bd6atLzXfAe4rPtYaiDwpJTRq7ehEOZ9fAwgY/HgBCmeZeoxTLvGZbzhoIbrFYtru4Q6mAhgw20B+NIoOfViQPwMnUfuJWPoqGVpmtjxzS1bZiKt3pyFo0//cYq/vl16qTyjIHCC6zIE38OJ1FuHJ/xphmVJtfeFBm1oI9tbVALQe0ZQ45K5dkdP4ehoPaMIU/uTX2WDRrE5h9/yLhHT8XQmT+ym6A1nCL5B6HRio27/p5aXnlLzYGnd9LlkvZsHJYfOiaDxPhe/wQAAP//ZxBMvQ==" + return "eJzkmU1v47YTxu/5FIMcA2w+gA9/IP+2hz0ULfpyJsbUWGEjkVxy6Kz76QuKkixLsizbspugOSywenme30MOJyLzBd5otwL8Ozh6AGDFBa3g8SX+//EBICMvnbKsjF7B/x4AID0LpclCEV9xVBB6WkGODwAbRUXmV9WDX0BjSXvx+MM7Gx91Jtj6yojDoUxXilVJuUOl2zuN5Bvt3o3LOtdHhdPPH68ELykGsVNyRLdxdORNcJIGht0MM+waHfCWpNoo6qL24x5E3lk6uHE88QmMBiW+DmYD3MEate5HXMC7HYahdhsYc//8NGpr1n+R5N6tdFFMgXUeESVaq3ReP//49HheiFQ2bYwKdlA08V9vcaRqzi7TVgo8FSS5UzeNmw/rVkKo7HrPriB8/XFgiNYWSuJifh29MbtMlaS9MvqwLI6UxIlymFsKE8gHnaMDNwBPj/jnp7O5N4XBIzcvpf45wYAjDk5TNsRFa4XSXuWv7E+2u7btr4lxHkF3mkd8ppqgZ3QsMuTxVjhyY0YvqkSH7zaepLOlHUlnx/3GqwXOaH79qoFFW18MUDOOlo7nw2Q3qZu+yX+9aBx9C+TZC2mC5mcfylHnsco4Yf1bUoZKeNp7g6qg7BbmSXmCIXhyTfig1bcwPvIXIPzpyU0Ye/JV17+J9+9J/GRuDPxKmuPioGxhhpeudvKb4Fk78+7JCVal0rkXmvjduDeRBVct3Gfc5kuR/T95Qe0FtRc0XrMAfbVS70Dnq/V7DpojSWpL4g5wtdV5fNYZGQtU53cZwL3deZhsGIu7EFZO03D0XVKld4NO/VOjPbE+O/71ON2QAJCb2YCCtlScQvLktjcnSiYTQJbcxrgStaQ0SbHFllQatxO4RVXguiCx3jH5Javp170tNLaQbKG1hcp2Pna9aIR1aot8L+raFWrXS6GlDcKSk6QZc0oL+W7s0gbYm6e1fW2Efwn+bGxlqhmL5MKTNDq7G/nXX1K1RD5I3oNdxloVhdL5LfYYtTSgjt86ceIzYlTF3A2qDM6RlrvRsbr4tGxUtZ1IR4zfhTS+Pw8Xz9APfa32CIYsOi5Js4gXlk25F4eB+D6sycJg93ulcS06PGLdf2XH/lNt+Rbc0KX6OrGRTNak+7GuN57cT9brIHYAZbLD0z24fsSbZZbkoSffnibIqj3coNTaZZ4cjtcbSg5YLLq4qpPOShYGso3txjiS6Hlx40b4uHUquIXPLupW2n237ZimtIFJbMveidfhH5QOKe5+arqnnAggvMSCPPHnSNLitvdHsmlGpckJpT3H39gfO1pNCw3tRCBHufLsdp8jUEM7ESjua9RnmaAadhAnQ8Y1ehJ1Z/7IaRrW5rfIIExptGLj5n+n5jO/UofC/W/S40vas3FxK/KBB7ZGbMf1nwAAAP//oQy/EA==" } diff --git a/x-pack/metricbeat/module/azure/module.yml b/x-pack/metricbeat/module/azure/module.yml index a51b202612b..4170154e246 100644 --- a/x-pack/metricbeat/module/azure/module.yml +++ b/x-pack/metricbeat/module/azure/module.yml @@ -4,5 +4,6 @@ metricsets: - container_instance - container_service - database_account + - app_state - compute_vm - compute_vm_scaleset diff --git a/x-pack/metricbeat/module/azure/test/integration.go b/x-pack/metricbeat/module/azure/test/integration.go index 7187e3b93f7..39e8402110c 100644 --- a/x-pack/metricbeat/module/azure/test/integration.go +++ b/x-pack/metricbeat/module/azure/test/integration.go @@ -40,3 +40,23 @@ func GetConfig(t *testing.T, metricSetName string) map[string]interface{} { "subscription_id": subId, } } + +// GetConfigForInsights function gets azure credentials for integration tests. +func GetConfigForInsights(t *testing.T, metricSetName string) map[string]interface{} { + t.Helper() + applicationId, ok := os.LookupEnv("AZURE_APPLICATION_ID") + if !ok { + t.Fatal("Could not find var AZURE_APPLICATION_ID") + } + apiKey, ok := os.LookupEnv("AZURE_API_KEY") + if !ok { + t.Fatal("Could not find var AZURE_API_KEY") + } + return map[string]interface{}{ + "module": "azure", + "period": "300s", + "metricsets": []string{metricSetName}, + "application_id": applicationId, + "api_key": apiKey, + } +} diff --git a/x-pack/metricbeat/modules.d/azure.yml.disabled b/x-pack/metricbeat/modules.d/azure.yml.disabled index 23211f47206..10d00e003cf 100644 --- a/x-pack/metricbeat/modules.d/azure.yml.disabled +++ b/x-pack/metricbeat/modules.d/azure.yml.disabled @@ -114,3 +114,11 @@ # api_key: '' # metrics: # - id: ["requests/count", "requests/duration"] + +#- module: azure +# metricsets: +# - app_state +# enabled: true +# period: 300s +# application_id: '' +# api_key: '' From 9aab0c0de9fd3ff44ae3307f174f256fa547e725 Mon Sep 17 00:00:00 2001 From: Mariana Dima Date: Mon, 5 Oct 2020 10:53:08 +0200 Subject: [PATCH 12/93] Map `cloud.account.id` to azure sub id (#21483) * mofidy doc * tet * add cloud * changelog --- CHANGELOG.next.asciidoc | 1 + .../add_cloud_metadata/provider_azure_vm.go | 3 + .../provider_azure_vm_test.go | 6 +- .../metricbeat/module/azure/add_metadata.go | 5 +- .../module/azure/compute_vm/_meta/data.json | 115 +++++++++++++----- .../azure/compute_vm_scaleset/_meta/data.json | 33 ++--- x-pack/metricbeat/module/azure/data.go | 4 +- 7 files changed, 120 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 68b77a46037..c72c4adb04b 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -731,6 +731,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Add overview and platform health dashboards to Cloud Foundry module. {pull}21124[21124] - Release lambda metricset in aws module as GA. {issue}21251[21251] {pull}21255[21255] - Add dashboard for pubsub metricset in googlecloud module. {pull}21326[21326] {issue}17137[17137] +- Map cloud data filed `cloud.account.id` to azure subscription. {pull}21483[21483] {issue}21381[21381] *Packetbeat* diff --git a/libbeat/processors/add_cloud_metadata/provider_azure_vm.go b/libbeat/processors/add_cloud_metadata/provider_azure_vm.go index 9cd3eba55b8..3028d531c1e 100644 --- a/libbeat/processors/add_cloud_metadata/provider_azure_vm.go +++ b/libbeat/processors/add_cloud_metadata/provider_azure_vm.go @@ -34,6 +34,9 @@ var azureVMMetadataFetcher = provider{ azHeaders := map[string]string{"Metadata": "true"} azSchema := func(m map[string]interface{}) common.MapStr { out, _ := s.Schema{ + "account": s.Object{ + "id": c.Str("subscriptionId"), + }, "instance": s.Object{ "id": c.Str("vmId"), "name": c.Str("name"), diff --git a/libbeat/processors/add_cloud_metadata/provider_azure_vm_test.go b/libbeat/processors/add_cloud_metadata/provider_azure_vm_test.go index 307ac60abad..a988cc8873f 100644 --- a/libbeat/processors/add_cloud_metadata/provider_azure_vm_test.go +++ b/libbeat/processors/add_cloud_metadata/provider_azure_vm_test.go @@ -40,7 +40,8 @@ const azInstanceIdentityDocument = `{ "sku": "14.04.4-LTS", "version": "14.04.201605091", "vmId": "04ab04c3-63de-4709-a9f9-9ab8c0411d5e", - "vmSize": "Standard_D3_v2" + "vmSize": "Standard_D3_v2", + "subscriptionId": "5tfb04c3-63de-4709-a9f9-9ab8c0411d5e" }` func initAzureTestServer() *httptest.Server { @@ -87,6 +88,9 @@ func TestRetrieveAzureMetadata(t *testing.T) { "machine": common.MapStr{ "type": "Standard_D3_v2", }, + "account": common.MapStr{ + "id": "5tfb04c3-63de-4709-a9f9-9ab8c0411d5e", + }, "region": "eastus2", }, } diff --git a/x-pack/metricbeat/module/azure/add_metadata.go b/x-pack/metricbeat/module/azure/add_metadata.go index ba8f35c7db6..1342170aec0 100644 --- a/x-pack/metricbeat/module/azure/add_metadata.go +++ b/x-pack/metricbeat/module/azure/add_metadata.go @@ -35,7 +35,7 @@ func addHostMetadata(event *mb.Event, metricList common.MapStr) { } } -func addCloudVMMetadata(event *mb.Event, vm VmResource) { +func addCloudVMMetadata(event *mb.Event, vm VmResource, subscriptionId string) { if vm.Name != "" { event.RootFields.Put("cloud.instance.name", vm.Name) event.RootFields.Put("host.name", vm.Name) @@ -47,4 +47,7 @@ func addCloudVMMetadata(event *mb.Event, vm VmResource) { if vm.Size != "" { event.RootFields.Put("cloud.machine.type", vm.Size) } + if subscriptionId != "" { + event.RootFields.Put("cloud.account.id", subscriptionId) + } } diff --git a/x-pack/metricbeat/module/azure/compute_vm/_meta/data.json b/x-pack/metricbeat/module/azure/compute_vm/_meta/data.json index 1da5cfb63ab..c38407c76f1 100644 --- a/x-pack/metricbeat/module/azure/compute_vm/_meta/data.json +++ b/x-pack/metricbeat/module/azure/compute_vm/_meta/data.json @@ -2,62 +2,86 @@ "@timestamp": "2017-10-12T08:05:34.853Z", "azure": { "compute_vm": { + "disk_read_bytes": { + "total": 0 + }, "disk_read_operations_per_sec": { - "avg": 3.3875 + "avg": 0 + }, + "disk_write_bytes": { + "total": 2969709.4 }, "disk_write_operations_per_sec": { - "avg": 0.6705 + "avg": 0.7809999999999999 }, "inbound_flows": { - "avg": 28.4 + "avg": 10 }, "inbound_flows_maximum_creation_rate": { - "avg": 10.4 + "avg": 10.6 + }, + "network_in": { + "total": 1478232 + }, + "network_in_total": { + "total": 1569665 + }, + "network_out": { + "total": 793344 + }, + "network_out_total": { + "total": 1074624 + }, + "os_disk_bandwidth_consumed_percentage": { + "avg": 0 + }, + "os_disk_iops_consumed_percentage": { + "avg": 0 }, "os_disk_queue_depth": { - "avg": 0.00125 + "avg": 0.002 }, "os_disk_read_bytes_per_sec": { - "avg": 602589.1825 + "avg": 0 }, "os_disk_read_operations_per_sec": { - "avg": 5.28375 + "avg": 0 }, "os_disk_write_bytes_per_sec": { - "avg": 14137.59375 + "avg": 9899.025 }, "os_disk_write_operations_per_sec": { - "avg": 1.46875 + "avg": 1.5619999999999998 }, "os_per_disk_qd": { - "avg": 0.00125 + "avg": 0.002 }, "os_per_disk_read_bytes_per_sec": { - "avg": 602589.1825 + "avg": 0 }, "os_per_disk_read_operations_per_sec": { - "avg": 5.28375 + "avg": 0 }, "os_per_disk_write_bytes_per_sec": { - "avg": 14137.59375 + "avg": 9899.025 }, "os_per_disk_write_operations_per_sec": { - "avg": 1.46875 + "avg": 1.5619999999999998 }, "outbound_flows": { - "avg": 28.4 + "avg": 10 }, "outbound_flows_maximum_creation_rate": { - "avg": 10.4 + "avg": 10.6 }, "per_disk_qd": { - "avg": 0.0025 + "avg": 0 }, "per_disk_read_bytes_per_sec": { - "avg": 51985.035 + "avg": 0 }, "per_disk_read_operations_per_sec": { - "avg": 2.92875 + "avg": 0 }, "per_disk_write_bytes_per_sec": { "avg": 0 @@ -66,26 +90,41 @@ "avg": 0 }, "percentage_cpu": { - "avg": 9.747 + "avg": 1.8719999999999999 + }, + "vm_cached_bandwidth_consumed_percentage": { + "avg": 0 + }, + "vm_cached_iops_consumed_percentage": { + "avg": 0 + }, + "vm_uncached_bandwidth_consumed_percentage": { + "avg": 0 + }, + "vm_uncached_iops_consumed_percentage": { + "avg": 0 } }, "namespace": "Microsoft.Compute/virtualMachines", "resource": { "group": "obs-infrastructure", - "id": "/subscriptions/70bd6e64-4b1e-4835-8896-db77b8eef364/resourceGroups/obs-infrastructure/providers/Microsoft.Compute/virtualMachines/obslinux", - "name": "obslinux", + "id": "/subscriptions/fd675b6f-b5e5-426e-ac45-d1f876d0ffa6/resourceGroups/obs-infrastructure/providers/Microsoft.Compute/virtualMachines/testaz", + "name": "testaz", "type": "Microsoft.Compute/virtualMachines" }, - "subscription_id": "70bd6e64-4b1e-4835-8896-db77b8eef364", + "subscription_id": "fd675b6f-b5e5-426e-ac45-d1f876d0ffa6", "timegrain": "PT5M" }, "cloud": { + "account": { + "id": "fd675b6f-b5e5-426e-ac45-d1f876d0ffa6" + }, "instance": { - "id": "d5d9444a-1964-4d23-9c62-5463ecb16fe0", - "name": "obslinux" + "id": "490fe4cf-2b33-4ead-a016-7e614c2f48ad", + "name": "testaz" }, "machine": { - "type": "Basic_A0" + "type": "Standard_A1_v2" }, "provider": "azure", "region": "westeurope" @@ -97,10 +136,28 @@ }, "host": { "cpu": { - "pct": 0.09747 + "pct": 0.01872 }, - "id": "d5d9444a-1964-4d23-9c62-5463ecb16fe0", - "name": "obslinux" + "disk": { + "read": { + "bytes": 0 + }, + "write": { + "bytes": 2969709.4 + } + }, + "id": "490fe4cf-2b33-4ead-a016-7e614c2f48ad", + "name": "testaz", + "network": { + "in": { + "bytes": 1569665, + "packets": 1478232 + }, + "out": { + "bytes": 1074624, + "packets": 793344 + } + } }, "metricset": { "name": "compute_vm", diff --git a/x-pack/metricbeat/module/azure/compute_vm_scaleset/_meta/data.json b/x-pack/metricbeat/module/azure/compute_vm_scaleset/_meta/data.json index e8f59859d8b..2d99d8ff6b0 100644 --- a/x-pack/metricbeat/module/azure/compute_vm_scaleset/_meta/data.json +++ b/x-pack/metricbeat/module/azure/compute_vm_scaleset/_meta/data.json @@ -2,6 +2,12 @@ "@timestamp": "2017-10-12T08:05:34.853Z", "azure": { "compute_vm_scaleset": { + "cpu_credits_consumed": { + "avg": 0.006999999999999999 + }, + "cpu_credits_remaining": { + "avg": 84.72 + }, "os_per_disk_qd": { "avg": 0 }, @@ -12,35 +18,34 @@ "avg": 0 }, "os_per_disk_write_bytes_per_sec": { - "avg": 1872.1200000000001 + "avg": 3846.531 }, "os_per_disk_write_operations_per_sec": { - "avg": 0.296 + "avg": 0.5519999999999999 } }, "namespace": "Microsoft.Compute/virtualMachineScaleSets", "resource": { - "group": "testgroup", - "id": "/subscriptions/70bd6e23-e3er3-4835-6785-db77b8eef364/resourceGroups/testgroup/providers/Microsoft.Compute/virtualMachineScaleSets/vmscaleset", - "name": "vmscaleset", - "tags": { - "environment": "staging", - "role": "allocator" - }, + "group": "obs-infrastructure", + "id": "/subscriptions/fd675b6f-b5e5-426e-ac45-d1f876d0ffa6/resourceGroups/obs-infrastructure/providers/Microsoft.Compute/virtualMachineScaleSets/obslinuxvmss", + "name": "obslinuxvmss", "type": "Microsoft.Compute/virtualMachineScaleSets" }, - "subscription_id": "70bd6e23-e3er3-4835-6785-db77b8eef364", + "subscription_id": "fd675b6f-b5e5-426e-ac45-d1f876d0ffa6", "timegrain": "PT5M" }, "cloud": { + "account": { + "id": "fd675b6f-b5e5-426e-ac45-d1f876d0ffa6" + }, "instance": { - "name": "vmscaleset" + "name": "obslinuxvmss" }, "machine": { - "type": "Standard_D4s_v3" + "type": "Standard_B1ls" }, "provider": "azure", - "region": "eastus2" + "region": "westeurope" }, "event": { "dataset": "azure.compute_vm_scaleset", @@ -48,7 +53,7 @@ "module": "azure" }, "host": { - "name": "vmscaleset" + "name": "obslinuxvmss" }, "metricset": { "name": "compute_vm_scaleset", diff --git a/x-pack/metricbeat/module/azure/data.go b/x-pack/metricbeat/module/azure/data.go index 4d479ef45d5..eb7f1433142 100644 --- a/x-pack/metricbeat/module/azure/data.go +++ b/x-pack/metricbeat/module/azure/data.go @@ -82,7 +82,7 @@ func EventsMapping(metrics []Metric, client *Client, report mb.ReporterV2) error event, metricList = createEvent(timestamp, defaultMetric, resource, groupDimValues) if client.Config.AddCloudMetadata { vm = client.GetVMForMetaData(&resource, groupDimValues) - addCloudVMMetadata(&event, vm) + addCloudVMMetadata(&event, vm, resource.Subscription) } } } @@ -90,7 +90,7 @@ func EventsMapping(metrics []Metric, client *Client, report mb.ReporterV2) error event, metricList = createEvent(timestamp, defaultMetric, resource, groupTimeValues) if client.Config.AddCloudMetadata { vm = client.GetVMForMetaData(&resource, groupTimeValues) - addCloudVMMetadata(&event, vm) + addCloudVMMetadata(&event, vm, resource.Subscription) } } if client.Config.DefaultResourceType == "" { From c91216796739bea67c32c6d6c5dc349abb7eb017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9mi=20V=C3=A1nyi?= Date: Mon, 5 Oct 2020 10:53:30 +0200 Subject: [PATCH 13/93] Do not run symlink tests on Windows (#21472) --- filebeat/input/filestream/fswatch_test.go | 146 +++++++----------- .../filestream/fswatch_test_non_windows.go | 144 +++++++++++++++++ .../input/filestream/testdata/excluded_file | 0 .../input/filestream/testdata/included_file | 0 .../testdata/symlink_to_included_file | 1 - 5 files changed, 201 insertions(+), 90 deletions(-) create mode 100644 filebeat/input/filestream/fswatch_test_non_windows.go delete mode 100644 filebeat/input/filestream/testdata/excluded_file delete mode 100644 filebeat/input/filestream/testdata/included_file delete mode 120000 filebeat/input/filestream/testdata/symlink_to_included_file diff --git a/filebeat/input/filestream/fswatch_test.go b/filebeat/input/filestream/fswatch_test.go index 5e63987c868..4979c9275c8 100644 --- a/filebeat/input/filestream/fswatch_test.go +++ b/filebeat/input/filestream/fswatch_test.go @@ -31,6 +31,12 @@ import ( "github.com/elastic/beats/v7/libbeat/logp" ) +var ( + excludedFilePath = filepath.Join("testdata", "excluded_file") + includedFilePath = filepath.Join("testdata", "included_file") + directoryPath = filepath.Join("testdata", "unharvestable_dir") +) + func TestFileScanner(t *testing.T) { testCases := map[string]struct { paths []string @@ -39,56 +45,30 @@ func TestFileScanner(t *testing.T) { expectedFiles []string }{ "select all files": { - paths: []string{ - filepath.Join("testdata", "excluded_file"), - filepath.Join("testdata", "included_file"), - }, + paths: []string{excludedFilePath, includedFilePath}, expectedFiles: []string{ - mustAbsPath(filepath.Join("testdata", "excluded_file")), - mustAbsPath(filepath.Join("testdata", "included_file")), + mustAbsPath(excludedFilePath), + mustAbsPath(includedFilePath), }, }, "skip excluded files": { - paths: []string{ - filepath.Join("testdata", "excluded_file"), - filepath.Join("testdata", "included_file"), - }, + paths: []string{excludedFilePath, includedFilePath}, excludedFiles: []match.Matcher{ - match.MustCompile(filepath.Join("testdata", "excluded_file")), - }, - expectedFiles: []string{ - mustAbsPath(filepath.Join("testdata", "included_file")), - }, - }, - // covers test_input.py/test_skip_symlinks - "skip symlinks": { - paths: []string{ - filepath.Join("testdata", "symlink_to_included_file"), - filepath.Join("testdata", "included_file"), + match.MustCompile("excluded_file"), }, - symlinks: false, expectedFiles: []string{ - mustAbsPath(filepath.Join("testdata", "included_file")), - }, - }, - "return a file once if symlinks are enabled": { - paths: []string{ - filepath.Join("testdata", "symlink_to_included_file"), - filepath.Join("testdata", "included_file"), - }, - symlinks: true, - expectedFiles: []string{ - mustAbsPath(filepath.Join("testdata", "included_file")), + mustAbsPath(includedFilePath), }, }, "skip directories": { - paths: []string{ - filepath.Join("testdata", "unharvestable_dir"), - }, + paths: []string{directoryPath}, expectedFiles: []string{}, }, } + setupFilesForScannerTest(t) + defer removeFilesOfScannerTest(t) + for name, test := range testCases { test := test @@ -107,11 +87,50 @@ func TestFileScanner(t *testing.T) { for p, _ := range files { paths = append(paths, p) } - assert.Equal(t, test.expectedFiles, paths) + assert.True(t, checkIfSameContents(test.expectedFiles, paths)) }) } } +func setupFilesForScannerTest(t *testing.T) { + err := os.MkdirAll(directoryPath, 0750) + if err != nil { + t.Fatal(t) + } + + for _, path := range []string{excludedFilePath, includedFilePath} { + f, err := os.Create(path) + if err != nil { + t.Fatalf("file %s, error %v", path, err) + } + f.Close() + } +} + +func removeFilesOfScannerTest(t *testing.T) { + err := os.RemoveAll("testdata") + if err != nil { + t.Fatal(err) + } +} + +// only handles sets +func checkIfSameContents(one, other []string) bool { + if len(one) != len(other) { + return false + } + + mustFind := len(one) + for _, oneElem := range one { + for _, otherElem := range other { + if oneElem == otherElem { + mustFind-- + } + } + } + return mustFind == 0 +} + func TestFileWatchNewDeleteModified(t *testing.T) { oldTs := time.Now() newTs := oldTs.Add(5 * time.Second) @@ -201,9 +220,7 @@ func TestFileWatchNewDeleteModified(t *testing.T) { events: make(chan loginp.FSEvent), } - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - go w.watch(ctx) + go w.watch(context.Background()) for _, expectedEvent := range test.expectedEvents { evt := w.Event() @@ -213,55 +230,6 @@ func TestFileWatchNewDeleteModified(t *testing.T) { } } -func TestFileWatcherRenamedFile(t *testing.T) { - testPath := mustAbsPath(filepath.Join("testdata", "first_name")) - renamedPath := mustAbsPath(filepath.Join("testdata", "renamed")) - - f, err := os.Create(testPath) - if err != nil { - t.Fatal(err) - } - f.Close() - fi, err := os.Stat(testPath) - if err != nil { - t.Fatal(err) - } - - cfg := fileScannerConfig{ - ExcludedFiles: nil, - Symlinks: false, - RecursiveGlob: false, - } - scanner, err := newFileScanner([]string{testPath, renamedPath}, cfg) - if err != nil { - t.Fatal(err) - } - w := fileWatcher{ - log: logp.L(), - scanner: scanner, - events: make(chan loginp.FSEvent), - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - go w.watch(ctx) - assert.Equal(t, loginp.FSEvent{Op: loginp.OpCreate, OldPath: "", NewPath: testPath, Info: fi}, w.Event()) - - err = os.Rename(testPath, renamedPath) - if err != nil { - t.Fatal(err) - } - defer os.Remove(renamedPath) - fi, err = os.Stat(renamedPath) - if err != nil { - t.Fatal(err) - } - - go w.watch(ctx) - assert.Equal(t, loginp.FSEvent{Op: loginp.OpRename, OldPath: testPath, NewPath: renamedPath, Info: fi}, w.Event()) -} - type mockScanner struct { files map[string]os.FileInfo } diff --git a/filebeat/input/filestream/fswatch_test_non_windows.go b/filebeat/input/filestream/fswatch_test_non_windows.go new file mode 100644 index 00000000000..eecfeddf930 --- /dev/null +++ b/filebeat/input/filestream/fswatch_test_non_windows.go @@ -0,0 +1,144 @@ +// 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. + +// +build !windows + +package filestream + +import ( + "context" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + + loginp "github.com/elastic/beats/v7/filebeat/input/filestream/internal/input-logfile" + "github.com/elastic/beats/v7/libbeat/common/match" + "github.com/elastic/beats/v7/libbeat/logp" +) + +func TestFileScannerSymlinks(t *testing.T) { + testCases := map[string]struct { + paths []string + excludedFiles []match.Matcher + symlinks bool + expectedFiles []string + }{ + // covers test_input.py/test_skip_symlinks + "skip symlinks": { + paths: []string{ + filepath.Join("testdata", "symlink_to_included_file"), + filepath.Join("testdata", "included_file"), + }, + symlinks: false, + expectedFiles: []string{ + mustAbsPath(filepath.Join("testdata", "included_file")), + }, + }, + "return a file once if symlinks are enabled": { + paths: []string{ + filepath.Join("testdata", "symlink_to_included_file"), + filepath.Join("testdata", "included_file"), + }, + symlinks: true, + expectedFiles: []string{ + mustAbsPath(filepath.Join("testdata", "included_file")), + }, + }, + } + + err := os.Symlink( + mustAbsPath(filepath.Join("testdata", "included_file")), + mustAbsPath(filepath.Join("testdata", "symlink_to_included_file")), + ) + if err != nil { + t.Fatal(err) + } + + for name, test := range testCases { + test := test + + t.Run(name, func(t *testing.T) { + cfg := fileScannerConfig{ + ExcludedFiles: test.excludedFiles, + Symlinks: true, + RecursiveGlob: false, + } + fs, err := newFileScanner(test.paths, cfg) + if err != nil { + t.Fatal(err) + } + files := fs.GetFiles() + paths := make([]string, 0) + for p, _ := range files { + paths = append(paths, p) + } + assert.Equal(t, test.expectedFiles, paths) + }) + } +} + +func TestFileWatcherRenamedFile(t *testing.T) { + testPath := mustAbsPath("first_name") + renamedPath := mustAbsPath("renamed") + + f, err := os.Create(testPath) + if err != nil { + t.Fatal(err) + } + f.Close() + fi, err := os.Stat(testPath) + if err != nil { + t.Fatal(err) + } + + cfg := fileScannerConfig{ + ExcludedFiles: nil, + Symlinks: false, + RecursiveGlob: false, + } + scanner, err := newFileScanner([]string{testPath, renamedPath}, cfg) + if err != nil { + t.Fatal(err) + } + w := fileWatcher{ + log: logp.L(), + scanner: scanner, + events: make(chan loginp.FSEvent), + } + + go w.watch(context.Background()) + assert.Equal(t, loginp.FSEvent{Op: loginp.OpCreate, OldPath: "", NewPath: testPath, Info: fi}, w.Event()) + + err = os.Rename(testPath, renamedPath) + if err != nil { + t.Fatal(err) + } + defer os.Remove(renamedPath) + fi, err = os.Stat(renamedPath) + if err != nil { + t.Fatal(err) + } + + go w.watch(context.Background()) + evt := w.Event() + + assert.Equal(t, loginp.OpRename, evt.Op) + assert.Equal(t, testPath, evt.OldPath) + assert.Equal(t, renamedPath, evt.NewPath) +} diff --git a/filebeat/input/filestream/testdata/excluded_file b/filebeat/input/filestream/testdata/excluded_file deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/filebeat/input/filestream/testdata/included_file b/filebeat/input/filestream/testdata/included_file deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/filebeat/input/filestream/testdata/symlink_to_included_file b/filebeat/input/filestream/testdata/symlink_to_included_file deleted file mode 120000 index 40824f3f7d3..00000000000 --- a/filebeat/input/filestream/testdata/symlink_to_included_file +++ /dev/null @@ -1 +0,0 @@ -filebeat/input/filestream/testdata/included_file \ No newline at end of file From 77163970c4e0b23148d583242e3aec477b03e275 Mon Sep 17 00:00:00 2001 From: Mariana Dima Date: Mon, 5 Oct 2020 12:03:20 +0200 Subject: [PATCH 14/93] Ignore unsupported metrics in the azure module (#21486) * mofidy doc * add ignore * changelog --- CHANGELOG.next.asciidoc | 1 + .../module/azure/compute_vm_scaleset/manifest.yml | 6 ++++++ x-pack/metricbeat/module/azure/config.go | 2 +- .../module/azure/container_instance/manifest.yml | 4 ++++ .../module/azure/container_service/manifest.yml | 6 ++++++ .../module/azure/database_account/manifest.yml | 8 ++++++++ .../module/azure/monitor/_meta/docs.asciidoc | 3 +++ .../metricbeat/module/azure/monitor/client_helper.go | 10 +++++----- .../module/azure/monitor/client_helper_test.go | 2 +- 9 files changed, 35 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index c72c4adb04b..2f956d70838 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -731,6 +731,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Add overview and platform health dashboards to Cloud Foundry module. {pull}21124[21124] - Release lambda metricset in aws module as GA. {issue}21251[21251] {pull}21255[21255] - Add dashboard for pubsub metricset in googlecloud module. {pull}21326[21326] {issue}17137[17137] +- Expand unsupported option from namespace to metrics in the azure module. {pull}21486[21486] - Map cloud data filed `cloud.account.id` to azure subscription. {pull}21483[21483] {issue}21381[21381] *Packetbeat* diff --git a/x-pack/metricbeat/module/azure/compute_vm_scaleset/manifest.yml b/x-pack/metricbeat/module/azure/compute_vm_scaleset/manifest.yml index 9369a36b79e..37b1293a2e5 100644 --- a/x-pack/metricbeat/module/azure/compute_vm_scaleset/manifest.yml +++ b/x-pack/metricbeat/module/azure/compute_vm_scaleset/manifest.yml @@ -12,8 +12,10 @@ input: - name: ["CPU Credits Remaining", "CPU Credits Consumed", "OS Per Disk Read Bytes/sec", "OS Per Disk Write Bytes/sec", "OS Per Disk Read Operations/Sec", "OS Per Disk Write Operations/Sec", "OS Per Disk QD"] namespace: "Microsoft.Compute/virtualMachineScaleSets" timegrain: "PT5M" + ignore_unsupported: true - name: ["Per Disk Read Bytes/sec", "Per Disk Write Bytes/sec", "Per Disk Read Operations/Sec", "Per Disk Write Operations/Sec", "Per Disk QD"] namespace: "Microsoft.Compute/virtualMachineScaleSets" + ignore_unsupported: true timegrain: "PT5M" dimensions: - name: "SlotId" @@ -24,6 +26,7 @@ input: "Premium Data Disk Cache Read Hit", "Outbound Flows Maximum Creation Rate", "Inbound Flows Maximum Creation Rate", "Outbound Flows", "Inbound Flows", "OS Disk IOPS Consumed Percentage", "OS Disk Bandwidth Consumed Percentage", "OS Disk Queue Depth", "OS Disk Write Operations/Sec", "OS Disk Read Operations/Sec", "OS Disk Write Bytes/sec", "OS Disk Read Bytes/sec", "Data Disk IOPS Consumed Percentage"] namespace: "Microsoft.Compute/virtualMachineScaleSets" + ignore_unsupported: true timegrain: "PT5M" dimensions: - name: "VMName" @@ -40,9 +43,11 @@ input: metrics: - name: ["CPU Credits Remaining", "CPU Credits Consumed", "OS Per Disk Read Bytes/sec", "OS Per Disk Write Bytes/sec", "OS Per Disk Read Operations/Sec", "OS Per Disk Write Operations/Sec", "OS Per Disk QD"] namespace: "Microsoft.Compute/virtualMachineScaleSets" + ignore_unsupported: true timegrain: "PT5M" - name: ["Per Disk Read Bytes/sec", "Per Disk Write Bytes/sec", "Per Disk Read Operations/Sec", "Per Disk Write Operations/Sec", "Per Disk QD"] namespace: "Microsoft.Compute/virtualMachineScaleSets" + ignore_unsupported: true timegrain: "PT5M" dimensions: - name: "SlotId" @@ -53,6 +58,7 @@ input: "Premium Data Disk Cache Read Hit", "Outbound Flows Maximum Creation Rate", "Inbound Flows Maximum Creation Rate", "Outbound Flows", "Inbound Flows", "OS Disk IOPS Consumed Percentage", "OS Disk Bandwidth Consumed Percentage", "OS Disk Queue Depth", "OS Disk Write Operations/Sec", "OS Disk Read Operations/Sec", "OS Disk Write Bytes/sec", "OS Disk Read Bytes/sec", "Data Disk IOPS Consumed Percentage"] namespace: "Microsoft.Compute/virtualMachineScaleSets" + ignore_unsupported: true timegrain: "PT5M" dimensions: - name: "VMName" diff --git a/x-pack/metricbeat/module/azure/config.go b/x-pack/metricbeat/module/azure/config.go index 63bb5450b57..00f1af56126 100644 --- a/x-pack/metricbeat/module/azure/config.go +++ b/x-pack/metricbeat/module/azure/config.go @@ -41,7 +41,7 @@ type MetricConfig struct { Dimensions []DimensionConfig `config:"dimensions"` Timegrain string `config:"timegrain"` // namespaces can be unsupported by some resources and supported in some, this configuration option makes sure no error messages are returned if namespace is unsupported - // info messages will be logged instead + // info messages will be logged instead. Same situation with metrics, some are being removed from the API, we would like to make sure that does not affect the module IgnoreUnsupported bool `config:"ignore_unsupported"` } diff --git a/x-pack/metricbeat/module/azure/container_instance/manifest.yml b/x-pack/metricbeat/module/azure/container_instance/manifest.yml index a0e6cd5ec27..c767e072a42 100644 --- a/x-pack/metricbeat/module/azure/container_instance/manifest.yml +++ b/x-pack/metricbeat/module/azure/container_instance/manifest.yml @@ -10,21 +10,25 @@ input: metrics: - name: ["CpuUsage", "MemoryUsage"] namespace: "Microsoft.ContainerInstance/containerGroups" + ignore_unsupported: true timegrain: "PT5M" dimensions: - name: "containerName" value: "*" - name: ["NetworkBytesReceivedPerSecond", "NetworkBytesTransmittedPerSecond"] namespace: "Microsoft.ContainerInstance/containerGroups" + ignore_unsupported: true timegrain: "PT5M" - resource_id: "" metrics: - name: ["CpuUsage", "MemoryUsage"] namespace: "Microsoft.ContainerInstance/containerGroups" + ignore_unsupported: true timegrain: "PT5M" dimensions: - name: "containerName" value: "*" - name: ["NetworkBytesReceivedPerSecond", "NetworkBytesTransmittedPerSecond"] namespace: "Microsoft.ContainerInstance/containerGroups" + ignore_unsupported: true timegrain: "PT5M" diff --git a/x-pack/metricbeat/module/azure/container_service/manifest.yml b/x-pack/metricbeat/module/azure/container_service/manifest.yml index 1384a268868..b7cf7639d42 100644 --- a/x-pack/metricbeat/module/azure/container_service/manifest.yml +++ b/x-pack/metricbeat/module/azure/container_service/manifest.yml @@ -10,6 +10,7 @@ input: metrics: - name: ["kube_node_status_condition"] namespace: "Microsoft.ContainerService/managedClusters" + ignore_unsupported: true timegrain: "PT5M" dimensions: - name: "node" @@ -20,9 +21,11 @@ input: value: "*" - name: ["kube_node_status_allocatable_cpu_cores", "kube_node_status_allocatable_memory_bytes"] namespace: "Microsoft.ContainerService/managedClusters" + ignore_unsupported: true timegrain: "PT5M" - name: ["kube_pod_status_ready", "kube_pod_status_phase"] namespace: "Microsoft.ContainerService/managedClusters" + ignore_unsupported: true timegrain: "PT5M" dimensions: - name: "pod" @@ -31,6 +34,7 @@ input: metrics: - name: ["kube_node_status_condition"] namespace: "Microsoft.ContainerService/managedClusters" + ignore_unsupported: true timegrain: "PT5M" dimensions: - name: "node" @@ -41,9 +45,11 @@ input: value: "*" - name: ["kube_node_status_allocatable_cpu_cores", "kube_node_status_allocatable_memory_bytes"] namespace: "Microsoft.ContainerService/managedClusters" + ignore_unsupported: true timegrain: "PT5M" - name: ["kube_pod_status_ready", "kube_pod_status_phase"] namespace: "Microsoft.ContainerService/managedClusters" + ignore_unsupported: true timegrain: "PT5M" dimensions: - name: "pod" diff --git a/x-pack/metricbeat/module/azure/database_account/manifest.yml b/x-pack/metricbeat/module/azure/database_account/manifest.yml index 39086f6ff66..3436008db7e 100644 --- a/x-pack/metricbeat/module/azure/database_account/manifest.yml +++ b/x-pack/metricbeat/module/azure/database_account/manifest.yml @@ -14,12 +14,14 @@ input: - name: ["AvailableStorage", "DataUsage","DocumentCount", "DocumentQuota", "IndexUsage", "MetadataRequests", "MongoRequestCharge", "MongoRequests", "MongoRequestsCount", "MongoRequestsInsert", "MongoRequestsDelete", "MongoRequestsQuery", "MongoRequestsUpdate","ProvisionedThroughput", "NormalizedRUConsumption"] namespace: "Microsoft.DocumentDb/databaseAccounts" + ignore_unsupported: true timegrain: "PT5M" dimensions: - name: "DatabaseName" value: "*" - name: ["TotalRequestUnits", "TotalRequests"] namespace: "Microsoft.DocumentDb/databaseAccounts" + ignore_unsupported: true timegrain: "PT5M" dimensions: - name: "DatabaseName" @@ -28,6 +30,7 @@ input: value: "*" - name: ["CassandraRequestCharges", "CassandraRequests"] namespace: "Microsoft.DocumentDb/databaseAccounts" + ignore_unsupported: true timegrain: "PT1M" dimensions: - name: "DatabaseName" @@ -38,6 +41,7 @@ input: "SqlContainerDelete", "SqlContainerThroughputUpdate", "SqlContainerUpdate", "SqlDatabaseDelete", "SqlDatabaseThroughputUpdate", "SqlDatabaseUpdate", "TableTableDelete", "TableTableThroughputUpdate","TableTableUpdate"] namespace: "Microsoft.DocumentDb/databaseAccounts" + ignore_unsupported: true dimensions: - name: "ResourceName" value: "*" @@ -49,12 +53,14 @@ input: - name: ["AvailableStorage", "DataUsage","DocumentCount", "DocumentQuota", "IndexUsage", "MetadataRequests", "MongoRequestCharge", "MongoRequests", "MongoRequestsCount", "MongoRequestsInsert", "MongoRequestsDelete", "MongoRequestsQuery", "MongoRequestsUpdate","ProvisionedThroughput", "NormalizedRUConsumption"] namespace: "Microsoft.DocumentDb/databaseAccounts" + ignore_unsupported: true timegrain: "PT5M" dimensions: - name: "DatabaseName" value: "*" - name: ["TotalRequestUnits", "TotalRequests"] namespace: "Microsoft.DocumentDb/databaseAccounts" + ignore_unsupported: true timegrain: "PT5M" dimensions: - name: "DatabaseName" @@ -63,6 +69,7 @@ input: value: "*" - name: ["CassandraRequestCharges", "CassandraRequests"] namespace: "Microsoft.DocumentDb/databaseAccounts" + ignore_unsupported: true timegrain: "PT1M" dimensions: - name: "DatabaseName" @@ -73,6 +80,7 @@ input: "SqlContainerDelete", "SqlContainerThroughputUpdate", "SqlContainerUpdate", "SqlDatabaseDelete", "SqlDatabaseThroughputUpdate", "SqlDatabaseUpdate", "TableTableDelete", "TableTableThroughputUpdate","TableTableUpdate"] namespace: "Microsoft.DocumentDb/databaseAccounts" + ignore_unsupported: true dimensions: - name: "ResourceName" value: "*" diff --git a/x-pack/metricbeat/module/azure/monitor/_meta/docs.asciidoc b/x-pack/metricbeat/module/azure/monitor/_meta/docs.asciidoc index 19254f4ada9..9957b6a2ddd 100644 --- a/x-pack/metricbeat/module/azure/monitor/_meta/docs.asciidoc +++ b/x-pack/metricbeat/module/azure/monitor/_meta/docs.asciidoc @@ -50,6 +50,9 @@ Metrics with dimensions are exported as flattened single dimensional metrics, ag `name`:: Dimension key `value`:: Dimension value. (Users can select * to return metric values for each dimension) +`ignore_unsupported`:: (_bool_) Namespaces can be unsupported by some resources and supported in some, this configuration option makes sure no error messages are returned if the namespace is unsupported. +The same will go for the metrics configured, some can be removed from Azure Monitor and it should not affect the state of the module. + Users can select the options to retrieve all metrics from a specific namespace using the following: ["source","yaml"] diff --git a/x-pack/metricbeat/module/azure/monitor/client_helper.go b/x-pack/metricbeat/module/azure/monitor/client_helper.go index 82875f46de5..403b971496f 100644 --- a/x-pack/metricbeat/module/azure/monitor/client_helper.go +++ b/x-pack/metricbeat/module/azure/monitor/client_helper.go @@ -78,20 +78,20 @@ func filterMetricNames(resourceId string, metricConfig azure.MetricConfig, metri } } else { // verify if configured metric names are valid, return log error event for the invalid ones, map only the valid metric names - supportedMetricNames, unsupportedMetricNames = filterSConfiguredMetrics(metricConfig.Name, metricDefinitions) - if len(unsupportedMetricNames) > 0 { + supportedMetricNames, unsupportedMetricNames = filterConfiguredMetrics(metricConfig.Name, metricDefinitions) + if len(unsupportedMetricNames) > 0 && !metricConfig.IgnoreUnsupported { return nil, errors.Errorf("the metric names configured %s are not supported for the resource %s and namespace %s", strings.Join(unsupportedMetricNames, ","), resourceId, metricConfig.Namespace) } } - if len(supportedMetricNames) == 0 { + if len(supportedMetricNames) == 0 && !metricConfig.IgnoreUnsupported { return nil, errors.Errorf("the metric names configured : %s are not supported for the resource %s and namespace %s ", strings.Join(metricConfig.Name, ","), resourceId, metricConfig.Namespace) } return supportedMetricNames, nil } -// filterSConfiguredMetrics will filter out any unsupported metrics based on the namespace selected -func filterSConfiguredMetrics(selectedRange []string, allRange []insights.MetricDefinition) ([]string, []string) { +// filterConfiguredMetrics will filter out any unsupported metrics based on the namespace selected +func filterConfiguredMetrics(selectedRange []string, allRange []insights.MetricDefinition) ([]string, []string) { var inRange []string var notInRange []string var allMetrics string diff --git a/x-pack/metricbeat/module/azure/monitor/client_helper_test.go b/x-pack/metricbeat/module/azure/monitor/client_helper_test.go index a15ee0089b9..bcbad0f4c26 100644 --- a/x-pack/metricbeat/module/azure/monitor/client_helper_test.go +++ b/x-pack/metricbeat/module/azure/monitor/client_helper_test.go @@ -108,7 +108,7 @@ func TestMapMetric(t *testing.T) { func TestFilterSConfiguredMetrics(t *testing.T) { selectedRange := []string{"TotalRequests", "Capacity", "CPUUsage"} - intersection, difference := filterSConfiguredMetrics(selectedRange, *MockMetricDefinitions()) + intersection, difference := filterConfiguredMetrics(selectedRange, *MockMetricDefinitions()) assert.Equal(t, intersection, []string{"TotalRequests", "Capacity"}) assert.Equal(t, difference, []string{"CPUUsage"}) } From 6fdb4ba899438c9fbb0e748e1a9643be5dd46437 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Mon, 5 Oct 2020 12:58:03 +0200 Subject: [PATCH 15/93] Skip filestream flaky tests (#21490) --- filebeat/input/filestream/fswatch_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/filebeat/input/filestream/fswatch_test.go b/filebeat/input/filestream/fswatch_test.go index 4979c9275c8..d6286a273eb 100644 --- a/filebeat/input/filestream/fswatch_test.go +++ b/filebeat/input/filestream/fswatch_test.go @@ -38,6 +38,8 @@ var ( ) func TestFileScanner(t *testing.T) { + t.Skip("Flaky test: https://github.com/elastic/beats/issues/21489") + testCases := map[string]struct { paths []string excludedFiles []match.Matcher @@ -132,6 +134,8 @@ func checkIfSameContents(one, other []string) bool { } func TestFileWatchNewDeleteModified(t *testing.T) { + t.Skip("Flaky test: https://github.com/elastic/beats/issues/21489") + oldTs := time.Now() newTs := oldTs.Add(5 * time.Second) testCases := map[string]struct { From 7400f430128ef7aa309c5d9ec4c9e5331f046aa4 Mon Sep 17 00:00:00 2001 From: kaiyan-sheng Date: Mon, 5 Oct 2020 07:29:25 -0600 Subject: [PATCH 16/93] [Metricbeat] Add latency config option into aws module (#20875) * Add latency config option into aws module --- CHANGELOG.next.asciidoc | 1 + metricbeat/docs/modules/aws.asciidoc | 11 +++++++++++ x-pack/metricbeat/module/aws/_meta/config.yml | 4 ++++ x-pack/metricbeat/module/aws/_meta/docs.asciidoc | 11 +++++++++++ x-pack/metricbeat/module/aws/aws.go | 3 +++ x-pack/metricbeat/module/aws/cloudwatch/cloudwatch.go | 3 ++- .../module/aws/cloudwatch/cloudwatch_test.go | 6 +++--- x-pack/metricbeat/module/aws/ec2/ec2.go | 3 ++- x-pack/metricbeat/module/aws/rds/rds.go | 3 ++- .../module/aws/s3_daily_storage/s3_daily_storage.go | 3 ++- x-pack/metricbeat/module/aws/s3_request/s3_request.go | 3 ++- x-pack/metricbeat/module/aws/sqs/sqs.go | 3 ++- x-pack/metricbeat/module/aws/utils.go | 7 ++++++- x-pack/metricbeat/module/aws/utils_test.go | 4 ++-- x-pack/metricbeat/modules.d/aws.yml.disabled | 4 ++++ 15 files changed, 57 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 2f956d70838..279eda229a7 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -724,6 +724,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Add billing data collection from Cost Explorer into aws billing metricset. {pull}20527[20527] {issue}20103[20103] - Migrate `compute_vm` metricset to a light one, map `cloud.instance.id` field. {pull}20889[20889] - Request prometheus endpoints to be gzipped by default {pull}20766[20766] +- Add latency config parameter into aws module. {pull}20875[20875] - Release all kubernetes `state` metricsets as GA {pull}20901[20901] - Add billing metricset into googlecloud module. {pull}20812[20812] {issue}20738[20738] - Move `compute_vm_scaleset` to light metricset. {pull}21038[21038] {issue}20985[20985] diff --git a/metricbeat/docs/modules/aws.asciidoc b/metricbeat/docs/modules/aws.asciidoc index 42d24c65ccd..e02a7a81460 100644 --- a/metricbeat/docs/modules/aws.asciidoc +++ b/metricbeat/docs/modules/aws.asciidoc @@ -19,16 +19,27 @@ module. Please see <> for more details. [float] == Module-specific configuration notes +* *AWS Credentials* + The `aws` module requires AWS credentials configuration in order to make AWS API calls. Users can either use `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and/or `AWS_SESSION_TOKEN`, or use shared AWS credentials file. Please see <> for more details. +* *regions* + This module also accepts optional configuration `regions` to specify which AWS regions to query metrics from. If the `regions` parameter is not set in the config file, then by default, the `aws` module will query metrics from all available AWS regions. +* *latency* + +Some AWS services send monitoring metrics to CloudWatch with a latency to +process larger than Metricbeat collection period. This case, please specify a +`latency` parameter so collection start time and end time will be shifted by the +given latency amount. + The aws module comes with a predefined dashboard. For example: image::./images/metricbeat-aws-overview.png[] diff --git a/x-pack/metricbeat/module/aws/_meta/config.yml b/x-pack/metricbeat/module/aws/_meta/config.yml index 618ed4cd854..6f604138505 100644 --- a/x-pack/metricbeat/module/aws/_meta/config.yml +++ b/x-pack/metricbeat/module/aws/_meta/config.yml @@ -44,4 +44,8 @@ period: 24h metricsets: - s3_daily_storage +- module: aws + period: 1m + latency: 5m + metricsets: - s3_request diff --git a/x-pack/metricbeat/module/aws/_meta/docs.asciidoc b/x-pack/metricbeat/module/aws/_meta/docs.asciidoc index fe9aeea007f..e4e55e82136 100644 --- a/x-pack/metricbeat/module/aws/_meta/docs.asciidoc +++ b/x-pack/metricbeat/module/aws/_meta/docs.asciidoc @@ -11,16 +11,27 @@ module. Please see <> for more details. [float] == Module-specific configuration notes +* *AWS Credentials* + The `aws` module requires AWS credentials configuration in order to make AWS API calls. Users can either use `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and/or `AWS_SESSION_TOKEN`, or use shared AWS credentials file. Please see <> for more details. +* *regions* + This module also accepts optional configuration `regions` to specify which AWS regions to query metrics from. If the `regions` parameter is not set in the config file, then by default, the `aws` module will query metrics from all available AWS regions. +* *latency* + +Some AWS services send monitoring metrics to CloudWatch with a latency to +process larger than Metricbeat collection period. This case, please specify a +`latency` parameter so collection start time and end time will be shifted by the +given latency amount. + The aws module comes with a predefined dashboard. For example: image::./images/metricbeat-aws-overview.png[] diff --git a/x-pack/metricbeat/module/aws/aws.go b/x-pack/metricbeat/module/aws/aws.go index f7b744c27cb..9786c4f7b38 100644 --- a/x-pack/metricbeat/module/aws/aws.go +++ b/x-pack/metricbeat/module/aws/aws.go @@ -27,6 +27,7 @@ import ( type Config struct { Period time.Duration `config:"period" validate:"nonzero,required"` Regions []string `config:"regions"` + Latency time.Duration `config:"latency"` AWSConfig awscommon.ConfigAWS `config:",inline"` TagsFilter []Tag `config:"tags_filter"` } @@ -37,6 +38,7 @@ type MetricSet struct { RegionsList []string Endpoint string Period time.Duration + Latency time.Duration AwsConfig *awssdk.Config AccountName string AccountID string @@ -87,6 +89,7 @@ func NewMetricSet(base mb.BaseMetricSet) (*MetricSet, error) { metricSet := MetricSet{ BaseMetricSet: base, Period: config.Period, + Latency: config.Latency, AwsConfig: &awsConfig, TagsFilter: config.TagsFilter, } diff --git a/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch.go b/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch.go index 42d68acb3af..07e1f09acef 100644 --- a/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch.go +++ b/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch.go @@ -141,7 +141,8 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { // of an error set the Error field of mb.Event or simply call report.Error(). func (m *MetricSet) Fetch(report mb.ReporterV2) error { // Get startTime and endTime - startTime, endTime := aws.GetStartTimeEndTime(m.Period) + startTime, endTime := aws.GetStartTimeEndTime(m.Period, m.Latency) + m.Logger().Debugf("startTime = %s, endTime = %s", startTime, endTime) // Check statistic method in config err := m.checkStatistics() diff --git a/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch_test.go b/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch_test.go index 353ffd0e236..393ddecb07e 100644 --- a/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch_test.go +++ b/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch_test.go @@ -1357,7 +1357,7 @@ func TestCreateEventsWithIdentifier(t *testing.T) { Value: "test-ec2", }, } - startTime, endTime := aws.GetStartTimeEndTime(m.MetricSet.Period) + startTime, endTime := aws.GetStartTimeEndTime(m.MetricSet.Period, m.MetricSet.Latency) events, err := m.createEvents(mockCloudwatchSvc, mockTaggingSvc, listMetricWithStatsTotal, resourceTypeTagFilters, regionName, startTime, endTime) assert.NoError(t, err) @@ -1399,7 +1399,7 @@ func TestCreateEventsWithoutIdentifier(t *testing.T) { } resourceTypeTagFilters := map[string][]aws.Tag{} - startTime, endTime := aws.GetStartTimeEndTime(m.MetricSet.Period) + startTime, endTime := aws.GetStartTimeEndTime(m.MetricSet.Period, m.MetricSet.Latency) events, err := m.createEvents(mockCloudwatchSvc, mockTaggingSvc, listMetricWithStatsTotal, resourceTypeTagFilters, regionName, startTime, endTime) assert.NoError(t, err) @@ -1447,7 +1447,7 @@ func TestCreateEventsWithTagsFilter(t *testing.T) { Value: "foo", }, } - startTime, endTime := aws.GetStartTimeEndTime(m.MetricSet.Period) + startTime, endTime := aws.GetStartTimeEndTime(m.MetricSet.Period, m.MetricSet.Latency) events, err := m.createEvents(mockCloudwatchSvc, mockTaggingSvc, listMetricWithStatsTotal, resourceTypeTagFilters, regionName, startTime, endTime) assert.NoError(t, err) diff --git a/x-pack/metricbeat/module/aws/ec2/ec2.go b/x-pack/metricbeat/module/aws/ec2/ec2.go index 36ad9a1ca02..4e0776072e6 100644 --- a/x-pack/metricbeat/module/aws/ec2/ec2.go +++ b/x-pack/metricbeat/module/aws/ec2/ec2.go @@ -88,7 +88,8 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { // of an error set the Error field of mb.Event or simply call report.Error(). func (m *MetricSet) Fetch(report mb.ReporterV2) error { // Get startTime and endTime - startTime, endTime := aws.GetStartTimeEndTime(m.Period) + startTime, endTime := aws.GetStartTimeEndTime(m.Period, m.Latency) + m.Logger().Debugf("startTime = %s, endTime = %s", startTime, endTime) for _, regionName := range m.MetricSet.RegionsList { awsConfig := m.MetricSet.AwsConfig.Copy() diff --git a/x-pack/metricbeat/module/aws/rds/rds.go b/x-pack/metricbeat/module/aws/rds/rds.go index f8bd907b3f6..b381dcac943 100644 --- a/x-pack/metricbeat/module/aws/rds/rds.go +++ b/x-pack/metricbeat/module/aws/rds/rds.go @@ -79,7 +79,8 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { // of an error set the Error field of mb.Event or simply call report.Error(). func (m *MetricSet) Fetch(report mb.ReporterV2) error { // Get startTime and endTime - startTime, endTime := aws.GetStartTimeEndTime(m.Period) + startTime, endTime := aws.GetStartTimeEndTime(m.Period, m.Latency) + m.Logger().Debugf("startTime = %s, endTime = %s", startTime, endTime) for _, regionName := range m.MetricSet.RegionsList { awsConfig := m.MetricSet.AwsConfig.Copy() diff --git a/x-pack/metricbeat/module/aws/s3_daily_storage/s3_daily_storage.go b/x-pack/metricbeat/module/aws/s3_daily_storage/s3_daily_storage.go index 7c9d453baca..53248284d41 100644 --- a/x-pack/metricbeat/module/aws/s3_daily_storage/s3_daily_storage.go +++ b/x-pack/metricbeat/module/aws/s3_daily_storage/s3_daily_storage.go @@ -69,7 +69,8 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { func (m *MetricSet) Fetch(report mb.ReporterV2) error { namespace := "AWS/S3" // Get startTime and endTime - startTime, endTime := aws.GetStartTimeEndTime(m.Period) + startTime, endTime := aws.GetStartTimeEndTime(m.Period, m.Latency) + m.Logger().Debugf("startTime = %s, endTime = %s", startTime, endTime) // GetMetricData for AWS S3 from Cloudwatch for _, regionName := range m.MetricSet.RegionsList { diff --git a/x-pack/metricbeat/module/aws/s3_request/s3_request.go b/x-pack/metricbeat/module/aws/s3_request/s3_request.go index afe53ac49ba..63b93f6cdf4 100644 --- a/x-pack/metricbeat/module/aws/s3_request/s3_request.go +++ b/x-pack/metricbeat/module/aws/s3_request/s3_request.go @@ -69,7 +69,8 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { func (m *MetricSet) Fetch(report mb.ReporterV2) error { namespace := "AWS/S3" // Get startTime and endTime - startTime, endTime := aws.GetStartTimeEndTime(m.Period) + startTime, endTime := aws.GetStartTimeEndTime(m.Period, m.Latency) + m.Logger().Debugf("startTime = %s, endTime = %s", startTime, endTime) // GetMetricData for AWS S3 from Cloudwatch for _, regionName := range m.MetricSet.RegionsList { diff --git a/x-pack/metricbeat/module/aws/sqs/sqs.go b/x-pack/metricbeat/module/aws/sqs/sqs.go index 7bc6a8349d0..6dc0774f66d 100644 --- a/x-pack/metricbeat/module/aws/sqs/sqs.go +++ b/x-pack/metricbeat/module/aws/sqs/sqs.go @@ -67,7 +67,8 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { func (m *MetricSet) Fetch(report mb.ReporterV2) error { namespace := "AWS/SQS" // Get startTime and endTime - startTime, endTime := aws.GetStartTimeEndTime(m.Period) + startTime, endTime := aws.GetStartTimeEndTime(m.Period, m.Latency) + m.Logger().Debugf("startTime = %s, endTime = %s", startTime, endTime) for _, regionName := range m.MetricSet.RegionsList { awsConfig := m.MetricSet.AwsConfig.Copy() diff --git a/x-pack/metricbeat/module/aws/utils.go b/x-pack/metricbeat/module/aws/utils.go index 67e5809bc8e..d1846083854 100644 --- a/x-pack/metricbeat/module/aws/utils.go +++ b/x-pack/metricbeat/module/aws/utils.go @@ -22,8 +22,13 @@ import ( ) // GetStartTimeEndTime function uses durationString to create startTime and endTime for queries. -func GetStartTimeEndTime(period time.Duration) (time.Time, time.Time) { +func GetStartTimeEndTime(period time.Duration, latency time.Duration) (time.Time, time.Time) { endTime := time.Now() + if latency != 0 { + // add latency if config is not 0 + endTime = endTime.Add(latency * -1) + } + // Set startTime double the period earlier than the endtime in order to // make sure GetMetricDataRequest gets the latest data point for each metric. return endTime.Add(period * -2), endTime diff --git a/x-pack/metricbeat/module/aws/utils_test.go b/x-pack/metricbeat/module/aws/utils_test.go index aef35f57e61..3c480d347db 100644 --- a/x-pack/metricbeat/module/aws/utils_test.go +++ b/x-pack/metricbeat/module/aws/utils_test.go @@ -176,7 +176,7 @@ func TestGetListMetricsOutputWithWildcard(t *testing.T) { } func TestGetMetricDataPerRegion(t *testing.T) { - startTime, endTime := GetStartTimeEndTime(10 * time.Minute) + startTime, endTime := GetStartTimeEndTime(10*time.Minute, 0) mockSvc := &MockCloudWatchClient{} var metricDataQueries []cloudwatch.MetricDataQuery @@ -205,7 +205,7 @@ func TestGetMetricDataPerRegion(t *testing.T) { } func TestGetMetricDataResults(t *testing.T) { - startTime, endTime := GetStartTimeEndTime(10 * time.Minute) + startTime, endTime := GetStartTimeEndTime(10*time.Minute, 0) mockSvc := &MockCloudWatchClient{} metricInfo := cloudwatch.Metric{ diff --git a/x-pack/metricbeat/modules.d/aws.yml.disabled b/x-pack/metricbeat/modules.d/aws.yml.disabled index d0053297885..cc3103643c7 100644 --- a/x-pack/metricbeat/modules.d/aws.yml.disabled +++ b/x-pack/metricbeat/modules.d/aws.yml.disabled @@ -47,4 +47,8 @@ period: 24h metricsets: - s3_daily_storage +- module: aws + period: 1m + latency: 5m + metricsets: - s3_request From d987d103ae888b46b3ae166fa7eebf99feb97a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9mi=20V=C3=A1nyi?= Date: Mon, 5 Oct 2020 15:44:00 +0200 Subject: [PATCH 17/93] Implementation of fileProspector (#21479) ## What does this PR do? This PR adds the implementation of `fileProspector`. The prospector listens for events from the `FSWatcher` and processes them depending on the type of the event. Possible actions are starting a new Harvester to read from a file, removing an entry from the registry, etc. --- filebeat/input/filestream/identifier.go | 139 ++++++++++++++ .../filestream/identifier_inode_deviceid.go | 108 +++++++++++ .../identifier_inode_deviceid_windows.go | 30 +++ filebeat/input/filestream/input.go | 6 + filebeat/input/filestream/prospector.go | 176 +++++++++++++++++- 5 files changed, 457 insertions(+), 2 deletions(-) create mode 100644 filebeat/input/filestream/identifier.go create mode 100644 filebeat/input/filestream/identifier_inode_deviceid.go create mode 100644 filebeat/input/filestream/identifier_inode_deviceid_windows.go diff --git a/filebeat/input/filestream/identifier.go b/filebeat/input/filestream/identifier.go new file mode 100644 index 00000000000..736c66da2f0 --- /dev/null +++ b/filebeat/input/filestream/identifier.go @@ -0,0 +1,139 @@ +// 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 filestream + +import ( + "fmt" + "os" + + loginp "github.com/elastic/beats/v7/filebeat/input/filestream/internal/input-logfile" + "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/common/file" +) + +const ( + nativeName = "native" + pathName = "path" + inodeMarkerName = "inode_marker" + + DefaultIdentifierName = nativeName + identitySep = "::" +) + +var ( + identifierFactories = map[string]identifierFactory{ + nativeName: newINodeDeviceIdentifier, + pathName: newPathIdentifier, + inodeMarkerName: newINodeMarkerIdentifier, + } +) + +type identifierFactory func(*common.Config) (fileIdentifier, error) + +type fileIdentifier interface { + GetSource(loginp.FSEvent) fileSource + Name() string +} + +// fileSource implements the Source interface +// It is required to identify and manage file sources. +type fileSource struct { + info os.FileInfo + newPath string + oldPath string + + name string + identifierGenerator string +} + +// Name returns the registry identifier of the file. +func (f fileSource) Name() string { + return f.name +} + +// newFileIdentifier creates a new state identifier for a log input. +func newFileIdentifier(ns *common.ConfigNamespace) (fileIdentifier, error) { + if ns == nil { + return newINodeDeviceIdentifier(nil) + } + + identifierType := ns.Name() + f, ok := identifierFactories[identifierType] + if !ok { + return nil, fmt.Errorf("no such file_identity generator: %s", identifierType) + } + + return f(ns.Config()) +} + +type inodeDeviceIdentifier struct { + name string +} + +func newINodeDeviceIdentifier(_ *common.Config) (fileIdentifier, error) { + return &inodeDeviceIdentifier{ + name: nativeName, + }, nil +} + +func (i *inodeDeviceIdentifier) GetSource(e loginp.FSEvent) fileSource { + return fileSource{ + info: e.Info, + newPath: e.NewPath, + oldPath: e.OldPath, + name: pluginName + identitySep + i.name + identitySep + file.GetOSState(e.Info).String(), + identifierGenerator: i.name, + } +} + +func (i *inodeDeviceIdentifier) Name() string { + return i.name +} + +type pathIdentifier struct { + name string +} + +func newPathIdentifier(_ *common.Config) (fileIdentifier, error) { + return &pathIdentifier{ + name: pathName, + }, nil +} + +func (p *pathIdentifier) GetSource(e loginp.FSEvent) fileSource { + return fileSource{ + info: e.Info, + newPath: e.NewPath, + oldPath: e.OldPath, + name: pluginName + identitySep + p.name + identitySep + e.NewPath, + identifierGenerator: p.name, + } +} + +func (p *pathIdentifier) Name() string { + return p.name +} + +// mockIdentifier is used for testing +type MockIdentifier struct{} + +func (m *MockIdentifier) GetSource(e loginp.FSEvent) fileSource { + return fileSource{identifierGenerator: "mock"} +} + +func (m *MockIdentifier) Name() string { return "mock" } diff --git a/filebeat/input/filestream/identifier_inode_deviceid.go b/filebeat/input/filestream/identifier_inode_deviceid.go new file mode 100644 index 00000000000..25254d97fdc --- /dev/null +++ b/filebeat/input/filestream/identifier_inode_deviceid.go @@ -0,0 +1,108 @@ +// 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. + +// +build !windows + +package filestream + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "time" + + loginp "github.com/elastic/beats/v7/filebeat/input/filestream/internal/input-logfile" + "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/common/file" + "github.com/elastic/beats/v7/libbeat/logp" +) + +type inodeMarkerIdentifier struct { + log *logp.Logger + name string + markerPath string + + markerFileLastModifitaion time.Time + markerTxt string +} + +func newINodeMarkerIdentifier(cfg *common.Config) (fileIdentifier, error) { + var config struct { + MarkerPath string `config:"path" validate:"required"` + } + err := cfg.Unpack(&config) + if err != nil { + return nil, fmt.Errorf("error while reading configuration of INode + marker file configuration: %v", err) + } + + fi, err := os.Stat(config.MarkerPath) + if err != nil { + return nil, fmt.Errorf("error while opening marker file at %s: %v", config.MarkerPath, err) + } + markerContent, err := ioutil.ReadFile(config.MarkerPath) + if err != nil { + return nil, fmt.Errorf("error while reading marker file at %s: %v", config.MarkerPath, err) + } + return &inodeMarkerIdentifier{ + log: logp.NewLogger("inode_marker_identifier_" + filepath.Base(config.MarkerPath)), + name: inodeMarkerName, + markerPath: config.MarkerPath, + markerFileLastModifitaion: fi.ModTime(), + markerTxt: string(markerContent), + }, nil +} + +func (i *inodeMarkerIdentifier) markerContents() string { + f, err := os.Open(i.markerPath) + if err != nil { + i.log.Errorf("Failed to open marker file %s: %v", i.markerPath, err) + return "" + } + defer f.Close() + + fi, err := f.Stat() + if err != nil { + i.log.Errorf("Failed to fetch file information for %s: %v", i.markerPath, err) + return "" + } + if i.markerFileLastModifitaion.Before(fi.ModTime()) { + contents, err := ioutil.ReadFile(i.markerPath) + if err != nil { + i.log.Errorf("Error while reading contents of marker file: %v", err) + return "" + } + i.markerTxt = string(contents) + } + + return i.markerTxt +} + +func (i *inodeMarkerIdentifier) GetSource(e loginp.FSEvent) fileSource { + osstate := file.GetOSState(e.Info) + return fileSource{ + info: e.Info, + newPath: e.NewPath, + oldPath: e.OldPath, + name: fmt.Sprintf("%s%s%s-%s", i.name, identitySep, osstate.InodeString(), i.markerContents()), + identifierGenerator: i.name, + } +} + +func (i *inodeMarkerIdentifier) Name() string { + return i.name +} diff --git a/filebeat/input/filestream/identifier_inode_deviceid_windows.go b/filebeat/input/filestream/identifier_inode_deviceid_windows.go new file mode 100644 index 00000000000..4ee8d866124 --- /dev/null +++ b/filebeat/input/filestream/identifier_inode_deviceid_windows.go @@ -0,0 +1,30 @@ +// 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. + +// +build windows + +package filestream + +import ( + "fmt" + + "github.com/elastic/beats/v7/libbeat/common" +) + +func newINodeMarkerIdentifier(cfg *common.Config) (fileIdentifier, error) { + return nil, fmt.Errorf("inode_deviceid is not supported on Windows") +} diff --git a/filebeat/input/filestream/input.go b/filebeat/input/filestream/input.go index 487a5f01c2a..bcd143c1c5a 100644 --- a/filebeat/input/filestream/input.go +++ b/filebeat/input/filestream/input.go @@ -29,6 +29,12 @@ import ( // are actively written by other applications. type filestream struct{} +type state struct { + Source string `json:"source" struct:"source"` + Offset int64 `json:"offset" struct:"offset"` + IdentifierName string `json:"identifier_name" struct:"identifier_name"` +} + const pluginName = "filestream" // Plugin creates a new filestream input plugin for creating a stateful input. diff --git a/filebeat/input/filestream/prospector.go b/filebeat/input/filestream/prospector.go index 257574b9ca1..94670e18ce7 100644 --- a/filebeat/input/filestream/prospector.go +++ b/filebeat/input/filestream/prospector.go @@ -18,19 +18,191 @@ package filestream import ( + "os" + "strings" + "time" + + "github.com/urso/sderr" + loginp "github.com/elastic/beats/v7/filebeat/input/filestream/internal/input-logfile" input "github.com/elastic/beats/v7/filebeat/input/v2" + "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/logp" "github.com/elastic/beats/v7/libbeat/statestore" + "github.com/elastic/go-concert/unison" +) + +const ( + prospectorDebugKey = "file_prospector" ) // fileProspector implements the Prospector interface. // It contains a file scanner which returns file system events. // The FS events then trigger either new Harvester runs or updates // the statestore. -type fileProspector struct{} +type fileProspector struct { + filewatcher loginp.FSWatcher + identifier fileIdentifier + ignoreOlder time.Duration + cleanRemoved bool +} + +func newFileProspector( + paths []string, + ignoreOlder time.Duration, + fileWatcherNs, identifierNs *common.ConfigNamespace, +) (loginp.Prospector, error) { + + filewatcher, err := newFileWatcher(paths, fileWatcherNs) + if err != nil { + return nil, err + } + + identifier, err := newFileIdentifier(identifierNs) + if err != nil { + return nil, err + } + + return &fileProspector{ + filewatcher: filewatcher, + identifier: identifier, + ignoreOlder: ignoreOlder, + cleanRemoved: true, + }, nil +} +// Run starts the fileProspector which accepts FS events from a file watcher. func (p *fileProspector) Run(ctx input.Context, s *statestore.Store, hg *loginp.HarvesterGroup) { - panic("TODO: implement me") + log := ctx.Logger.With("prospector", prospectorDebugKey) + log.Debug("Starting prospector") + defer log.Debug("Prospector has stopped") + + if p.cleanRemoved { + p.cleanRemovedBetweenRuns(log, s) + } + + p.updateIdentifiersBetweenRuns(log, s) + + var tg unison.MultiErrGroup + + tg.Go(func() error { + p.filewatcher.Run(ctx.Cancelation) + return nil + }) + + tg.Go(func() error { + for ctx.Cancelation.Err() == nil { + fe := p.filewatcher.Event() + + if fe.Op == loginp.OpDone { + return nil + } + + src := p.identifier.GetSource(fe) + switch fe.Op { + case loginp.OpCreate: + log.Debugf("A new file %s has been found", fe.NewPath) + + if p.ignoreOlder > 0 { + now := time.Now() + if now.Sub(fe.Info.ModTime()) > p.ignoreOlder { + log.Debugf("Ignore file because ignore_older reached. File %s", fe.NewPath) + break + } + } + + hg.Run(ctx, src) + + case loginp.OpWrite: + log.Debugf("File %s has been updated", fe.NewPath) + + hg.Run(ctx, src) + + case loginp.OpDelete: + log.Debugf("File %s has been removed", fe.OldPath) + + if p.cleanRemoved { + log.Debugf("Remove state for file as file removed: %s", fe.OldPath) + + err := s.Remove(src.Name()) + if err != nil { + log.Errorf("Error while removing state from statestore: %v", err) + } + } + + case loginp.OpRename: + log.Debugf("File %s has been renamed to %s", fe.OldPath, fe.NewPath) + // TODO update state information in the store + + default: + log.Error("Unkown return value %v", fe.Op) + } + } + return nil + }) + + errs := tg.Wait() + if len(errs) > 0 { + log.Error("%s", sderr.WrapAll(errs, "running prospector failed")) + } +} + +func (p *fileProspector) cleanRemovedBetweenRuns(log *logp.Logger, s *statestore.Store) { + keyPrefix := pluginName + "::" + s.Each(func(key string, dec statestore.ValueDecoder) (bool, error) { + if !strings.HasPrefix(string(key), keyPrefix) { + return true, nil + } + + var st state + if err := dec.Decode(&st); err != nil { + log.Errorf("Failed to read regisry state for '%v', cursor state will be ignored. Error was: %+v", + key, err) + return true, nil + } + + _, err := os.Stat(st.Source) + if err != nil { + s.Remove(key) + } + + return true, nil + }) +} + +func (p *fileProspector) updateIdentifiersBetweenRuns(log *logp.Logger, s *statestore.Store) { + keyPrefix := pluginName + "::" + s.Each(func(key string, dec statestore.ValueDecoder) (bool, error) { + if !strings.HasPrefix(string(key), keyPrefix) { + return true, nil + } + + var st state + if err := dec.Decode(&st); err != nil { + log.Errorf("Failed to read regisry state for '%v', cursor state will be ignored. Error was: %+v", key, err) + return true, nil + } + + if st.IdentifierName == p.identifier.Name() { + return true, nil + } + + fi, err := os.Stat(st.Source) + if err != nil { + return true, nil + } + newKey := p.identifier.GetSource(loginp.FSEvent{NewPath: st.Source, Info: fi}).Name() + st.IdentifierName = p.identifier.Name() + + err = s.Set(newKey, st) + if err != nil { + log.Errorf("Failed to add updated state for '%v', cursor state will be ignored. Error was: %+v", key, err) + return true, nil + } + s.Remove(key) + + return true, nil + }) } func (p *fileProspector) Test() error { From eb119c95c1c6300960b203fdae28c92603ff8145 Mon Sep 17 00:00:00 2001 From: Michal Pristas Date: Mon, 5 Oct 2020 16:09:20 +0200 Subject: [PATCH 18/93] [Ingest Manager] Download asc from artifact store specified in spec (#21488) [Ingest Manager] Download asc from artifact store specified in spec (#21488) --- .../pkg/agent/application/upgrade/step_download.go | 2 +- .../elastic-agent/pkg/agent/operation/common_test.go | 2 +- .../elastic-agent/pkg/agent/operation/monitoring.go | 2 +- .../pkg/agent/operation/operation_verify.go | 2 +- .../pkg/artifact/download/composed/verifier.go | 4 ++-- .../pkg/artifact/download/fs/verifier.go | 2 +- .../pkg/artifact/download/fs/verifier_test.go | 6 +++--- .../pkg/artifact/download/http/elastic_test.go | 2 +- .../pkg/artifact/download/http/verifier.go | 12 ++++++------ .../elastic-agent/pkg/artifact/download/verifier.go | 2 +- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/x-pack/elastic-agent/pkg/agent/application/upgrade/step_download.go b/x-pack/elastic-agent/pkg/agent/application/upgrade/step_download.go index 28e93949fbf..9db442d3655 100644 --- a/x-pack/elastic-agent/pkg/agent/application/upgrade/step_download.go +++ b/x-pack/elastic-agent/pkg/agent/application/upgrade/step_download.go @@ -31,7 +31,7 @@ func (u *Upgrader) downloadArtifact(ctx context.Context, version, sourceURI stri return "", errors.New(err, "failed upgrade of agent binary") } - matches, err := verifier.Verify(agentName, version) + matches, err := verifier.Verify(agentName, version, agentArtifactName) if err != nil { return "", errors.New(err, "failed verification of agent binary") } diff --git a/x-pack/elastic-agent/pkg/agent/operation/common_test.go b/x-pack/elastic-agent/pkg/agent/operation/common_test.go index cc17733c656..6e9b042fe92 100644 --- a/x-pack/elastic-agent/pkg/agent/operation/common_test.go +++ b/x-pack/elastic-agent/pkg/agent/operation/common_test.go @@ -143,7 +143,7 @@ var _ download.Downloader = &DummyDownloader{} type DummyVerifier struct{} -func (*DummyVerifier) Verify(p, v string) (bool, error) { +func (*DummyVerifier) Verify(p, v, _ string) (bool, error) { return true, nil } diff --git a/x-pack/elastic-agent/pkg/agent/operation/monitoring.go b/x-pack/elastic-agent/pkg/agent/operation/monitoring.go index fe33de852d1..c4d895eb6ee 100644 --- a/x-pack/elastic-agent/pkg/agent/operation/monitoring.go +++ b/x-pack/elastic-agent/pkg/agent/operation/monitoring.go @@ -161,7 +161,7 @@ func (o *Operator) generateMonitoringSteps(version string, output interface{}) [ ProgramSpec: program.Spec{ Name: metricsProcessName, Cmd: metricsProcessName, - Artifact: fmt.Sprintf("%s/%s", artifactPrefix, logsProcessName), + Artifact: fmt.Sprintf("%s/%s", artifactPrefix, metricsProcessName), }, Meta: map[string]interface{}{ configrequest.MetaConfigKey: mbConfig, diff --git a/x-pack/elastic-agent/pkg/agent/operation/operation_verify.go b/x-pack/elastic-agent/pkg/agent/operation/operation_verify.go index 289693ca373..97cf906cace 100644 --- a/x-pack/elastic-agent/pkg/agent/operation/operation_verify.go +++ b/x-pack/elastic-agent/pkg/agent/operation/operation_verify.go @@ -66,7 +66,7 @@ func (o *operationVerify) Run(_ context.Context, application Application) (err e } }() - isVerified, err := o.verifier.Verify(o.program.BinaryName(), o.program.Version()) + isVerified, err := o.verifier.Verify(o.program.BinaryName(), o.program.Version(), o.program.ArtifactName()) if err != nil { return errors.New(err, fmt.Sprintf("operation '%s' failed to verify %s.%s", o.Name(), o.program.BinaryName(), o.program.Version()), diff --git a/x-pack/elastic-agent/pkg/artifact/download/composed/verifier.go b/x-pack/elastic-agent/pkg/artifact/download/composed/verifier.go index 33397a87e1e..9d6c4477733 100644 --- a/x-pack/elastic-agent/pkg/artifact/download/composed/verifier.go +++ b/x-pack/elastic-agent/pkg/artifact/download/composed/verifier.go @@ -29,11 +29,11 @@ func NewVerifier(verifiers ...download.Verifier) *Verifier { } // Verify checks the package from configured source. -func (e *Verifier) Verify(programName, version string) (bool, error) { +func (e *Verifier) Verify(programName, version, artifactName string) (bool, error) { var err error for _, v := range e.vv { - b, e := v.Verify(programName, version) + b, e := v.Verify(programName, version, artifactName) if e == nil { return b, nil } diff --git a/x-pack/elastic-agent/pkg/artifact/download/fs/verifier.go b/x-pack/elastic-agent/pkg/artifact/download/fs/verifier.go index d934b20faef..09462ef3f23 100644 --- a/x-pack/elastic-agent/pkg/artifact/download/fs/verifier.go +++ b/x-pack/elastic-agent/pkg/artifact/download/fs/verifier.go @@ -51,7 +51,7 @@ func NewVerifier(config *artifact.Config, allowEmptyPgp bool, pgp []byte) (*Veri // Verify checks downloaded package on preconfigured // location agains a key stored on elastic.co website. -func (v *Verifier) Verify(programName, version string) (bool, error) { +func (v *Verifier) Verify(programName, version, artifactName string) (bool, error) { filename, err := artifact.GetArtifactName(programName, version, v.config.OS(), v.config.Arch()) if err != nil { return false, errors.New(err, "retrieving package name") diff --git a/x-pack/elastic-agent/pkg/artifact/download/fs/verifier_test.go b/x-pack/elastic-agent/pkg/artifact/download/fs/verifier_test.go index 4fd845482c5..975d9ecb14d 100644 --- a/x-pack/elastic-agent/pkg/artifact/download/fs/verifier_test.go +++ b/x-pack/elastic-agent/pkg/artifact/download/fs/verifier_test.go @@ -65,7 +65,7 @@ func TestFetchVerify(t *testing.T) { // first download verify should fail: // download skipped, as invalid package is prepared upfront // verify fails and cleans download - matches, err := verifier.Verify(programName, version) + matches, err := verifier.Verify(programName, version, artifactName) assert.NoError(t, err) assert.Equal(t, false, matches) @@ -88,7 +88,7 @@ func TestFetchVerify(t *testing.T) { _, err = os.Stat(hashTargetFilePath) assert.NoError(t, err) - matches, err = verifier.Verify(programName, version) + matches, err = verifier.Verify(programName, version, artifactName) assert.NoError(t, err) assert.Equal(t, true, matches) } @@ -162,7 +162,7 @@ func TestVerify(t *testing.T) { t.Fatal(err) } - isOk, err := testVerifier.Verify(beatName, version) + isOk, err := testVerifier.Verify(beatName, version, artifactName) if err != nil { t.Fatal(err) } diff --git a/x-pack/elastic-agent/pkg/artifact/download/http/elastic_test.go b/x-pack/elastic-agent/pkg/artifact/download/http/elastic_test.go index 0edb979a320..fec1d991c88 100644 --- a/x-pack/elastic-agent/pkg/artifact/download/http/elastic_test.go +++ b/x-pack/elastic-agent/pkg/artifact/download/http/elastic_test.go @@ -110,7 +110,7 @@ func TestVerify(t *testing.T) { t.Fatal(err) } - isOk, err := testVerifier.Verify(beatName, version) + isOk, err := testVerifier.Verify(beatName, version, artifactName) if err != nil { t.Fatal(err) } diff --git a/x-pack/elastic-agent/pkg/artifact/download/http/verifier.go b/x-pack/elastic-agent/pkg/artifact/download/http/verifier.go index 9f2eacd9395..c0dfef9b30e 100644 --- a/x-pack/elastic-agent/pkg/artifact/download/http/verifier.go +++ b/x-pack/elastic-agent/pkg/artifact/download/http/verifier.go @@ -59,7 +59,7 @@ func NewVerifier(config *artifact.Config, allowEmptyPgp bool, pgp []byte) (*Veri // Verify checks downloaded package on preconfigured // location agains a key stored on elastic.co website. -func (v *Verifier) Verify(programName, version string) (bool, error) { +func (v *Verifier) Verify(programName, version, artifactName string) (bool, error) { // TODO: think about verifying asc for prepacked beats filename, err := artifact.GetArtifactName(programName, version, v.config.OS(), v.config.Arch()) @@ -81,7 +81,7 @@ func (v *Verifier) Verify(programName, version string) (bool, error) { return isMatch, err } - return v.verifyAsc(programName, version) + return v.verifyAsc(programName, version, artifactName) } func (v *Verifier) verifyHash(filename, fullPath string) (bool, error) { @@ -127,7 +127,7 @@ func (v *Verifier) verifyHash(filename, fullPath string) (bool, error) { return expectedHash == computedHash, nil } -func (v *Verifier) verifyAsc(programName, version string) (bool, error) { +func (v *Verifier) verifyAsc(programName, version, artifactName string) (bool, error) { if len(v.pgpBytes) == 0 { // no pgp available skip verification process return true, nil @@ -143,7 +143,7 @@ func (v *Verifier) verifyAsc(programName, version string) (bool, error) { return false, errors.New(err, "retrieving package path") } - ascURI, err := v.composeURI(programName, filename) + ascURI, err := v.composeURI(filename, artifactName) if err != nil { return false, errors.New(err, "composing URI for fetching asc file", errors.TypeNetwork) } @@ -177,7 +177,7 @@ func (v *Verifier) verifyAsc(programName, version string) (bool, error) { } -func (v *Verifier) composeURI(programName, filename string) (string, error) { +func (v *Verifier) composeURI(filename, artifactName string) (string, error) { upstream := v.config.SourceURI if !strings.HasPrefix(upstream, "http") && !strings.HasPrefix(upstream, "file") && !strings.HasPrefix(upstream, "/") { // always default to https @@ -190,7 +190,7 @@ func (v *Verifier) composeURI(programName, filename string) (string, error) { return "", errors.New(err, "invalid upstream URI", errors.TypeNetwork, errors.M(errors.MetaKeyURI, upstream)) } - uri.Path = path.Join(uri.Path, "beats", programName, filename+ascSuffix) + uri.Path = path.Join(uri.Path, artifactName, filename+ascSuffix) return uri.String(), nil } diff --git a/x-pack/elastic-agent/pkg/artifact/download/verifier.go b/x-pack/elastic-agent/pkg/artifact/download/verifier.go index 6aa4dc4abe4..491979514ea 100644 --- a/x-pack/elastic-agent/pkg/artifact/download/verifier.go +++ b/x-pack/elastic-agent/pkg/artifact/download/verifier.go @@ -6,5 +6,5 @@ package download // Verifier is an interface verifying GPG key of a downloaded artifact type Verifier interface { - Verify(programName, version string) (bool, error) + Verify(programName, version, artifactName string) (bool, error) } From 641d02f5feab7b959e8013821b2e1144c92a43dd Mon Sep 17 00:00:00 2001 From: Marius Iversen Date: Mon, 5 Oct 2020 16:52:50 +0200 Subject: [PATCH 19/93] [Filebeat][New Fileset] Cisco Umbrella support (#21504) * initial MVP push for cisco umbrella * Initial commit for Cisco Umbrella ready for review * updating some wording in the cisco docs for umbrella --- filebeat/docs/fields.asciidoc | 147 +++++++++++ filebeat/docs/modules/cisco.asciidoc | 59 ++++- .../filebeat/module/cisco/_meta/docs.asciidoc | 59 ++++- x-pack/filebeat/module/cisco/fields.go | 2 +- .../module/cisco/umbrella/_meta/fields.yml | 61 +++++ .../module/cisco/umbrella/config/input.yml | 23 ++ .../module/cisco/umbrella/ingest/pipeline.yml | 246 ++++++++++++++++++ .../module/cisco/umbrella/manifest.yml | 8 + .../test/umbrella-cloudfirewalllogs.log | 2 + ...brella-cloudfirewalllogs.log-expected.json | 74 ++++++ .../cisco/umbrella/test/umbrella-dnslogs.log | 2 + .../test/umbrella-dnslogs.log-expected.json | 92 +++++++ .../cisco/umbrella/test/umbrella-iplogs.log | 2 + .../test/umbrella-iplogs.log-expected.json | 60 +++++ .../umbrella/test/umbrella-proxylogs.log | 3 + .../test/umbrella-proxylogs.log-expected.json | 115 ++++++++ 16 files changed, 952 insertions(+), 3 deletions(-) create mode 100644 x-pack/filebeat/module/cisco/umbrella/_meta/fields.yml create mode 100644 x-pack/filebeat/module/cisco/umbrella/config/input.yml create mode 100644 x-pack/filebeat/module/cisco/umbrella/ingest/pipeline.yml create mode 100644 x-pack/filebeat/module/cisco/umbrella/manifest.yml create mode 100644 x-pack/filebeat/module/cisco/umbrella/test/umbrella-cloudfirewalllogs.log create mode 100644 x-pack/filebeat/module/cisco/umbrella/test/umbrella-cloudfirewalllogs.log-expected.json create mode 100644 x-pack/filebeat/module/cisco/umbrella/test/umbrella-dnslogs.log create mode 100644 x-pack/filebeat/module/cisco/umbrella/test/umbrella-dnslogs.log-expected.json create mode 100644 x-pack/filebeat/module/cisco/umbrella/test/umbrella-iplogs.log create mode 100644 x-pack/filebeat/module/cisco/umbrella/test/umbrella-iplogs.log-expected.json create mode 100644 x-pack/filebeat/module/cisco/umbrella/test/umbrella-proxylogs.log create mode 100644 x-pack/filebeat/module/cisco/umbrella/test/umbrella-proxylogs.log-expected.json diff --git a/filebeat/docs/fields.asciidoc b/filebeat/docs/fields.asciidoc index 8a145ff8724..b66c1163367 100644 --- a/filebeat/docs/fields.asciidoc +++ b/filebeat/docs/fields.asciidoc @@ -26581,6 +26581,153 @@ type: keyword -- This key captures values or decorators used within a registry entry +type: keyword + +-- + +[float] +=== cisco.umbrella + +Fields for Cisco Umbrella. + + + +*`cisco.umbrella.identities`*:: ++ +-- +An array of the different identities related to the event. + + +type: keyword + +-- + +*`cisco.umbrella.categories`*:: ++ +-- +The security or content categories that the destination matches. + + +type: keyword + +-- + +*`cisco.umbrella.policy_identity_type`*:: ++ +-- +The first identity type matched with this request. Available in version 3 and above. + + +type: keyword + +-- + +*`cisco.umbrella.identity_types`*:: ++ +-- +The type of identity that made the request. For example, Roaming Computer or Network. + + +type: keyword + +-- + +*`cisco.umbrella.blocked_categories`*:: ++ +-- +The categories that resulted in the destination being blocked. Available in version 4 and above. + + +type: keyword + +-- + +*`cisco.umbrella.content_type`*:: ++ +-- +The type of web content, typically text/html. + + +type: keyword + +-- + +*`cisco.umbrella.sha_sha256`*:: ++ +-- +Hex digest of the response content. + + +type: keyword + +-- + +*`cisco.umbrella.av_detections`*:: ++ +-- +The detection name according to the antivirus engine used in file inspection. + + +type: keyword + +-- + +*`cisco.umbrella.puas`*:: ++ +-- +A list of all potentially unwanted application (PUA) results for the proxied file as returned by the antivirus scanner. + + +type: keyword + +-- + +*`cisco.umbrella.amp_disposition`*:: ++ +-- +The status of the files proxied and scanned by Cisco Advanced Malware Protection (AMP) as part of the Umbrella File Inspection feature; can be Clean, Malicious or Unknown. + + +type: keyword + +-- + +*`cisco.umbrella.amp_malware_name`*:: ++ +-- +If Malicious, the name of the malware according to AMP. + + +type: keyword + +-- + +*`cisco.umbrella.amp_score`*:: ++ +-- +The score of the malware from AMP. This field is not currently used and will be blank. + + +type: keyword + +-- + +*`cisco.umbrella.datacenter`*:: ++ +-- +The name of the Umbrella Data Center that processed the user-generated traffic. + + +type: keyword + +-- + +*`cisco.umbrella.origin_id`*:: ++ +-- +The unique identity of the network tunnel. + + type: keyword -- diff --git a/filebeat/docs/modules/cisco.asciidoc b/filebeat/docs/modules/cisco.asciidoc index c12f818caca..d087826245e 100644 --- a/filebeat/docs/modules/cisco.asciidoc +++ b/filebeat/docs/modules/cisco.asciidoc @@ -10,13 +10,15 @@ This file is generated! See scripts/docs_collector.py == Cisco module -This is a module for Cisco network device's logs. It includes the following +This is a module for Cisco network device's logs and Cisco Umbrella. It includes the following filesets for receiving logs over syslog or read from a file: - `asa` fileset: supports Cisco ASA firewall logs. - `ftd` fileset: supports Cisco Firepower Threat Defense logs. - `ios` fileset: supports Cisco IOS router and switch logs. - `nexus` fileset: supports Cisco Nexus switch logs. +- `meraki` fileset: supports Cisco Meraki logs. +- `umbrella` fileset: supports Cisco Umbrella logs. Cisco ASA devices also support exporting flow records using NetFlow, which is supported by the {filebeat-ref}/filebeat-module-netflow.html[netflow module] in @@ -32,6 +34,8 @@ The module is by default configured to run via syslog on port 9001 for ASA and port 9002 for IOS. However it can also be configured to read from a file path. See the following example. +Cisco Umbrella publishes its logs in a compressed CSV format to a S3 bucket. + ["source","yaml",subs="attributes"] ----- - module: cisco @@ -379,6 +383,59 @@ will be found under `rsa.raw`. The default is false. :fileset_ex!: +[float] +==== `umbrella` fileset settings + +The Cisco Umbrella fileset primarily focuses on reading CSV files from an S3 bucket using the filebeat S3 input. + +To configure Cisco Umbrella to log to either your own S3 bucket or one that is managed by Cisco please follow the https://docs.umbrella.com/deployment-umbrella/docs/log-management[Cisco Umbrella User Guide.] + +This fileset supports all 4 log types: +- Proxy +- Cloud Firewall +- IP Logs +- DNS logs + +The Cisco Umbrella fileset depends on the original file path structure being followed. This structure is documented https://docs.umbrella.com/deployment-umbrella/docs/log-formats-and-versioning[Umbrella Log Formats and Versioning]: + +/--

/--
---.csv.gz +dnslogs/--/----.csv.gz + +When configuring the fileset, please ensure that the Queue URL is set to the root folder that includes each of these subfolders above. + +Example config: + +[source,yaml] +---- +- module: cisco + umbrella: + enabled: true + var.input: s3 + var.queue_url: https://sqs.us-east-1.amazonaws.com/ID/CiscoQueue + var.access_key_id: 123456 + var.secret_access_key: PASSWORD +---- + +*`var.input`*:: + +The input from which messages are read. Can be S3 or file. + +*`var.queue_url`*:: + +The URL to the SQS queue if the input type is S3. + +*`var.access_key_id`*:: + +The ID for the access key used to read from the SQS queue. + +*`var.secret_access_key`*:: + +The secret token used for authenticating to the SQS queue. + +:has-dashboards!: + +:fileset_ex!: + [float] === Example dashboard diff --git a/x-pack/filebeat/module/cisco/_meta/docs.asciidoc b/x-pack/filebeat/module/cisco/_meta/docs.asciidoc index 08dc160fab0..aaf285680e0 100644 --- a/x-pack/filebeat/module/cisco/_meta/docs.asciidoc +++ b/x-pack/filebeat/module/cisco/_meta/docs.asciidoc @@ -5,13 +5,15 @@ == Cisco module -This is a module for Cisco network device's logs. It includes the following +This is a module for Cisco network device's logs and Cisco Umbrella. It includes the following filesets for receiving logs over syslog or read from a file: - `asa` fileset: supports Cisco ASA firewall logs. - `ftd` fileset: supports Cisco Firepower Threat Defense logs. - `ios` fileset: supports Cisco IOS router and switch logs. - `nexus` fileset: supports Cisco Nexus switch logs. +- `meraki` fileset: supports Cisco Meraki logs. +- `umbrella` fileset: supports Cisco Umbrella logs. Cisco ASA devices also support exporting flow records using NetFlow, which is supported by the {filebeat-ref}/filebeat-module-netflow.html[netflow module] in @@ -27,6 +29,8 @@ The module is by default configured to run via syslog on port 9001 for ASA and port 9002 for IOS. However it can also be configured to read from a file path. See the following example. +Cisco Umbrella publishes its logs in a compressed CSV format to a S3 bucket. + ["source","yaml",subs="attributes"] ----- - module: cisco @@ -374,6 +378,59 @@ will be found under `rsa.raw`. The default is false. :fileset_ex!: +[float] +==== `umbrella` fileset settings + +The Cisco Umbrella fileset primarily focuses on reading CSV files from an S3 bucket using the filebeat S3 input. + +To configure Cisco Umbrella to log to either your own S3 bucket or one that is managed by Cisco please follow the https://docs.umbrella.com/deployment-umbrella/docs/log-management[Cisco Umbrella User Guide.] + +This fileset supports all 4 log types: +- Proxy +- Cloud Firewall +- IP Logs +- DNS logs + +The Cisco Umbrella fileset depends on the original file path structure being followed. This structure is documented https://docs.umbrella.com/deployment-umbrella/docs/log-formats-and-versioning[Umbrella Log Formats and Versioning]: + +/--
/--
---.csv.gz +dnslogs/--/----.csv.gz + +When configuring the fileset, please ensure that the Queue URL is set to the root folder that includes each of these subfolders above. + +Example config: + +[source,yaml] +---- +- module: cisco + umbrella: + enabled: true + var.input: s3 + var.queue_url: https://sqs.us-east-1.amazonaws.com/ID/CiscoQueue + var.access_key_id: 123456 + var.secret_access_key: PASSWORD +---- + +*`var.input`*:: + +The input from which messages are read. Can be S3 or file. + +*`var.queue_url`*:: + +The URL to the SQS queue if the input type is S3. + +*`var.access_key_id`*:: + +The ID for the access key used to read from the SQS queue. + +*`var.secret_access_key`*:: + +The secret token used for authenticating to the SQS queue. + +:has-dashboards!: + +:fileset_ex!: + [float] === Example dashboard diff --git a/x-pack/filebeat/module/cisco/fields.go b/x-pack/filebeat/module/cisco/fields.go index fac83c30f27..de57daee50f 100644 --- a/x-pack/filebeat/module/cisco/fields.go +++ b/x-pack/filebeat/module/cisco/fields.go @@ -19,5 +19,5 @@ func init() { // AssetCisco returns asset data. // This is the base64 encoded gzipped contents of module/cisco. func AssetCisco() string { - return "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" + return "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" } diff --git a/x-pack/filebeat/module/cisco/umbrella/_meta/fields.yml b/x-pack/filebeat/module/cisco/umbrella/_meta/fields.yml new file mode 100644 index 00000000000..e45d12ef732 --- /dev/null +++ b/x-pack/filebeat/module/cisco/umbrella/_meta/fields.yml @@ -0,0 +1,61 @@ +- name: cisco.umbrella + type: group + description: > + Fields for Cisco Umbrella. + fields: + - name: identities + type: keyword + description: > + An array of the different identities related to the event. + - name: categories + type: keyword + description: > + The security or content categories that the destination matches. + - name: policy_identity_type + type: keyword + description: > + The first identity type matched with this request. Available in version 3 and above. + - name: identity_types + type: keyword + description: > + The type of identity that made the request. For example, Roaming Computer or Network. + - name: blocked_categories + type: keyword + description: > + The categories that resulted in the destination being blocked. Available in version 4 and above. + - name: content_type + type: keyword + description: > + The type of web content, typically text/html. + - name: sha_sha256 + type: keyword + description: > + Hex digest of the response content. + - name: av_detections + type: keyword + description: > + The detection name according to the antivirus engine used in file inspection. + - name: puas + type: keyword + description: > + A list of all potentially unwanted application (PUA) results for the proxied file as returned by the antivirus scanner. + - name: amp_disposition + type: keyword + description: > + The status of the files proxied and scanned by Cisco Advanced Malware Protection (AMP) as part of the Umbrella File Inspection feature; can be Clean, Malicious or Unknown. + - name: amp_malware_name + type: keyword + description: > + If Malicious, the name of the malware according to AMP. + - name: amp_score + type: keyword + description: > + The score of the malware from AMP. This field is not currently used and will be blank. + - name: datacenter + type: keyword + description: > + The name of the Umbrella Data Center that processed the user-generated traffic. + - name: origin_id + type: keyword + description: > + The unique identity of the network tunnel. diff --git a/x-pack/filebeat/module/cisco/umbrella/config/input.yml b/x-pack/filebeat/module/cisco/umbrella/config/input.yml new file mode 100644 index 00000000000..8b0ccde6e2e --- /dev/null +++ b/x-pack/filebeat/module/cisco/umbrella/config/input.yml @@ -0,0 +1,23 @@ +{{ if eq .input "s3" }} + +type: s3 +queue_url: {{ .queue_url }} +access_key_id: {{ .access_key_id }} +secret_access_key: {{ .secret_access_key }} + +{{ else if eq .input "file" }} + +type: log +paths: +{{ range $i, $path := .paths }} + - {{$path}} +{{ end }} +exclude_files: [".gz$"] + +{{ end }} + +processors: +- add_fields: + target: '' + fields: + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/cisco/umbrella/ingest/pipeline.yml b/x-pack/filebeat/module/cisco/umbrella/ingest/pipeline.yml new file mode 100644 index 00000000000..2a602ff2331 --- /dev/null +++ b/x-pack/filebeat/module/cisco/umbrella/ingest/pipeline.yml @@ -0,0 +1,246 @@ +description: Pipeline for parsing cisco umbrella logs +processors: +- set: + field: observer.vendor + value: Cisco +- set: + field: observer.product + value: Umbrella +- set: + field: event.ingested + value: "{{_ingest.timestamp}}" +- set: + field: event.original + value: "{{message}}" +############ +# DNS Logs # +############ +- csv: + field: message + target_fields: + - cisco.umbrella._tmp_time + - source.user.name + - cisco.umbrella.identities + - source.address + - destination.address + - cisco.umbrella.action + - dns.question.type + - dns.response_code + - destination.domain + - cisco.umbrella.categories + - cisco.umbrella.policy_identity_type + - cisco.umbrella.identity_types + - cisco.umbrella.blocked_categories + if: ctx?.log?.file?.path.contains('dnslogs') + +- set: + field: observer.type + value: dns + if: ctx?.log?.file?.path.contains('dnslogs') +########### +# IP Logs # +########### +- csv: + field: message + target_fields: + - cisco.umbrella._tmp_time + - source.user.name + - source.address + - source.port + - destination.address + - destination.port + - cisco.umbrella.categories + if: ctx?.log?.file?.path.contains('iplogs') + +- set: + field: observer.type + value: firewall + if: ctx?.log?.file?.path.contains('iplogs') + +############## +# Proxy Logs # +############## +- csv: + field: message + target_fields: + - cisco.umbrella._tmp_time + - cisco.umbrella.identities + - source.address + - source.nat.ip + - destination.address + - cisco.umbrella.content_type + - cisco.umbrella.verdict + - url.full + - http.request.referrer + - user_agent.original + - http.response.status_code + - http.request.bytes + - http.response.bytes + - http.response.body.bytes + - cisco.umbrella.sha_sha256 + - cisco.umbrella.categories + - cisco.umbrella.av_detections + - cisco.umbrella.puas + - cisco.umbrella.amp_disposition + - cisco.umbrella.amp_malware_name + - cisco.umbrella.amp_score + - cisco.umbrella.identity_types + - cisco.umbrella.blocked_categories + if: ctx?.log?.file?.path.contains('proxylogs') + +- set: + field: observer.type + value: proxy + if: ctx?.log?.file?.path.contains('proxylogs') + +####################### +# Cloud Firewall Logs # +####################### +- csv: + field: message + target_fields: + - cisco.umbrella._tmp_time + - cisco.umbrella.origin_id + - source.user.name + - cisco.umbrella.identity_types + - cisco.umbrella.direction + - network.transport + - source.bytes + - source.address + - source.port + - destination.address + - destination.port + - cisco.umbrella.datacenter + - cisco.umbrella.ruleid + - cisco.umbrella.verdict + if: ctx?.log?.file?.path.contains('cloudfirewalllogs') + +- set: + field: observer.type + value: firewall + if: ctx?.log?.file?.path.contains('cloudfirewalllogs') + +# Identifies is a field that includes any sort of username, device or other asset that is included in the request. +# Converting this to an array to make it easier to use in searches and visualizations +- split: + field: cisco.umbrella.identities + separator: "," + preserve_trailing: false + if: "ctx?.log?.file?.path.contains('dnslogs') && ctx?.cisco?.umbrella?.identities != null" + +###################### +# General ECS Fields # +###################### +# This field is always in UTC, so no timezone should need to be set +- date: + field: cisco.umbrella._tmp_time + target_field: "@timestamp" + formats: + - "yyyy-MM-dd HH:mm:ss" + +################## +# DNS ECS Fields # +################## +- set: + field: dns.type + value: query + if: ctx?.cisco?.umbrella?.action != null + +###################### +# Network ECS Fields # +###################### +- lowercase: + field: cisco.umbrella.direction + target_field: network.direction + if: ctx?.cisco?.umbrella?.direction != null + +################### +# Rule ECS Fields # +################### +- rename: + field: cisco.umbrella.ruleid + target_field: rule.id + if: ctx?.cisco?.umbrella?.ruleid != null + +#################### +# Event ECS Fields # +#################### +- set: + field: event.action + value: "dns-request-{{cisco.umbrella.action}}" + if: ctx?.cisco?.umbrella?.action != null +- set: + field: event.category + value: network + if: ctx?.cisco?.umbrella?.action != null +- append: + field: event.type + value: allowed + if: "ctx?.cisco?.umbrella?.action == 'Allowed' || ctx?.cisco?.umbrella?.verdict == 'ALLOWED' || ctx?.cisco?.umbrella?.verdict == 'ALLOW'" +- append: + field: event.type + value: denied + if: "ctx?.cisco?.umbrella?.action == 'Blocked' || ctx?.cisco?.umbrella?.verdict == 'BLOCKED' || ctx?.cisco?.umbrella?.verdict == 'BLOCK'" +- append: + field: event.type + value: connection + if: ctx?.cisco?.umbrella?.action != null + +# Converting address fields to either ip or domain +- grok: + field: source.address + patterns: + - "(?:%{IP:source.ip}|%{GREEDYDATA:source.domain})" + ignore_failure: true +- grok: + field: destination.address + patterns: + - "(?:%{IP:destination.ip}|%{GREEDYDATA:destination.domain})" + ignore_failure: true + +###################### +# Related ECS Fields # +###################### +- append: + field: related.user + value: "{{source.user.name}}" + if: ctx?.source?.user?.name != null +- append: + field: related.ip + value: "{{source.ip}}" + if: ctx?.source?.ip != null +- append: + field: related.ip + value: "{{source.nat.ip}}" + if: ctx?.source?.nat?.ip != null +- append: + field: related.ip + value: "{{destination.ip}}" + if: ctx?.destination?.ip != null +- append: + field: related.hosts + value: "{{source.domain}}" + if: ctx?.source?.domain != null +- append: + field: related.hosts + value: "{{destination.domain}}" + if: ctx?.destination?.domain != null +- append: + field: related.hash + value: "{{cisco.umbrella.sha_sha256}}" + if: ctx?.cisco?.umbrella?.sha_sha256 != null + +########### +# Cleanup # +########### +- remove: + field: + - cisco.umbrella._tmp_time + - cisco.umbrella.direction + - cisco.umbrella.action + - cisco.umbrella.verdict + ignore_missing: true +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/cisco/umbrella/manifest.yml b/x-pack/filebeat/module/cisco/umbrella/manifest.yml new file mode 100644 index 00000000000..3a7150e714d --- /dev/null +++ b/x-pack/filebeat/module/cisco/umbrella/manifest.yml @@ -0,0 +1,8 @@ +module_version: "1.0" + +var: + - name: tags + default: [cisco-umbrella, forwarded] + +ingest_pipeline: ingest/pipeline.yml +input: config/input.yml diff --git a/x-pack/filebeat/module/cisco/umbrella/test/umbrella-cloudfirewalllogs.log b/x-pack/filebeat/module/cisco/umbrella/test/umbrella-cloudfirewalllogs.log new file mode 100644 index 00000000000..3e5f23fced2 --- /dev/null +++ b/x-pack/filebeat/module/cisco/umbrella/test/umbrella-cloudfirewalllogs.log @@ -0,0 +1,2 @@ +2020-07-23 18:03:46,[211039844],Passive Monitor,CDFW Tunnel Device,OUTBOUND,1,84,172.17.3.4,,146.112.255.129,,ams1.edc,12,ALLOW +2020-07-23 18:03:46,[211039844],Passive Monitor,CDFW Tunnel Device,INBOUND,1,84,172.17.3.4,,146.112.255.129,,ams1.edc,12,BLOCK diff --git a/x-pack/filebeat/module/cisco/umbrella/test/umbrella-cloudfirewalllogs.log-expected.json b/x-pack/filebeat/module/cisco/umbrella/test/umbrella-cloudfirewalllogs.log-expected.json new file mode 100644 index 00000000000..65aabab5a88 --- /dev/null +++ b/x-pack/filebeat/module/cisco/umbrella/test/umbrella-cloudfirewalllogs.log-expected.json @@ -0,0 +1,74 @@ +[ + { + "@timestamp": "2020-07-23T18:03:46.000Z", + "cisco.umbrella.datacenter": "ams1.edc", + "cisco.umbrella.identity_types": "CDFW Tunnel Device", + "cisco.umbrella.origin_id": "[211039844]", + "destination.address": "146.112.255.129", + "destination.ip": "146.112.255.129", + "event.dataset": "cisco.umbrella", + "event.module": "cisco", + "event.original": "2020-07-23 18:03:46,[211039844],Passive Monitor,CDFW Tunnel Device,OUTBOUND,1,84,172.17.3.4,,146.112.255.129,,ams1.edc,12,ALLOW", + "event.type": [ + "allowed" + ], + "fileset.name": "umbrella", + "input.type": "log", + "log.offset": 0, + "message": "2020-07-23 18:03:46,[211039844],Passive Monitor,CDFW Tunnel Device,OUTBOUND,1,84,172.17.3.4,,146.112.255.129,,ams1.edc,12,ALLOW", + "network.direction": "outbound", + "network.transport": "1", + "observer.product": "Umbrella", + "observer.type": "firewall", + "observer.vendor": "Cisco", + "related.ip": [ + "172.17.3.4", + "146.112.255.129" + ], + "related.user": [ + "Passive Monitor" + ], + "rule.id": "12", + "service.type": "cisco", + "source.address": "172.17.3.4", + "source.bytes": "84", + "source.ip": "172.17.3.4", + "source.user.name": "Passive Monitor" + }, + { + "@timestamp": "2020-07-23T18:03:46.000Z", + "cisco.umbrella.datacenter": "ams1.edc", + "cisco.umbrella.identity_types": "CDFW Tunnel Device", + "cisco.umbrella.origin_id": "[211039844]", + "destination.address": "146.112.255.129", + "destination.ip": "146.112.255.129", + "event.dataset": "cisco.umbrella", + "event.module": "cisco", + "event.original": "2020-07-23 18:03:46,[211039844],Passive Monitor,CDFW Tunnel Device,INBOUND,1,84,172.17.3.4,,146.112.255.129,,ams1.edc,12,BLOCK", + "event.type": [ + "denied" + ], + "fileset.name": "umbrella", + "input.type": "log", + "log.offset": 128, + "message": "2020-07-23 18:03:46,[211039844],Passive Monitor,CDFW Tunnel Device,INBOUND,1,84,172.17.3.4,,146.112.255.129,,ams1.edc,12,BLOCK", + "network.direction": "inbound", + "network.transport": "1", + "observer.product": "Umbrella", + "observer.type": "firewall", + "observer.vendor": "Cisco", + "related.ip": [ + "172.17.3.4", + "146.112.255.129" + ], + "related.user": [ + "Passive Monitor" + ], + "rule.id": "12", + "service.type": "cisco", + "source.address": "172.17.3.4", + "source.bytes": "84", + "source.ip": "172.17.3.4", + "source.user.name": "Passive Monitor" + } +] \ No newline at end of file diff --git a/x-pack/filebeat/module/cisco/umbrella/test/umbrella-dnslogs.log b/x-pack/filebeat/module/cisco/umbrella/test/umbrella-dnslogs.log new file mode 100644 index 00000000000..403c1c9df33 --- /dev/null +++ b/x-pack/filebeat/module/cisco/umbrella/test/umbrella-dnslogs.log @@ -0,0 +1,2 @@ +"2020-07-23 23:49:54","elasticuser","elasticuser2","some other identity","192.168.1.1","8.8.8.8","Allowed","1 (A)","NOERROR","elastic.co.","Software/Technology,Business Services,Application","Test Policy Name","SomeIdentityType","" +"2020-07-23 23:50:25","elasticuser","elasticuser2","some other identity","192.168.1.1","4.4.4.4","Blocked","1 (A)","NOERROR","elastic.co/something.","Chat,Instant Messaging,Block List,Application","Test Policy Name","SomeIdentityType","BlockedCategories" diff --git a/x-pack/filebeat/module/cisco/umbrella/test/umbrella-dnslogs.log-expected.json b/x-pack/filebeat/module/cisco/umbrella/test/umbrella-dnslogs.log-expected.json new file mode 100644 index 00000000000..81b1478da27 --- /dev/null +++ b/x-pack/filebeat/module/cisco/umbrella/test/umbrella-dnslogs.log-expected.json @@ -0,0 +1,92 @@ +[ + { + "@timestamp": "2020-07-23T23:49:54.000Z", + "cisco.umbrella.blocked_categories": "SomeIdentityType", + "cisco.umbrella.categories": "elastic.co.", + "cisco.umbrella.identities": [ + "elasticuser2" + ], + "cisco.umbrella.identity_types": "Test Policy Name", + "cisco.umbrella.policy_identity_type": "Software/Technology,Business Services,Application", + "destination.address": "192.168.1.1", + "destination.domain": "NOERROR", + "destination.ip": "192.168.1.1", + "dns.question.type": "Allowed", + "dns.response_code": "1 (A)", + "dns.type": "query", + "event.action": "dns-request-8.8.8.8", + "event.category": "network", + "event.dataset": "cisco.umbrella", + "event.module": "cisco", + "event.original": "\\\"2020-07-23 23:49:54\\\",\\\"elasticuser\\\",\\\"elasticuser2\\\",\\\"some other identity\\\",\\\"192.168.1.1\\\",\\\"8.8.8.8\\\",\\\"Allowed\\\",\\\"1 (A)\\\",\\\"NOERROR\\\",\\\"elastic.co.\\\",\\\"Software/Technology,Business Services,Application\\\",\\\"Test Policy Name\\\",\\\"SomeIdentityType\\\",\\\"\\\"", + "event.type": [ + "connection" + ], + "fileset.name": "umbrella", + "input.type": "log", + "log.offset": 0, + "message": "\"2020-07-23 23:49:54\",\"elasticuser\",\"elasticuser2\",\"some other identity\",\"192.168.1.1\",\"8.8.8.8\",\"Allowed\",\"1 (A)\",\"NOERROR\",\"elastic.co.\",\"Software/Technology,Business Services,Application\",\"Test Policy Name\",\"SomeIdentityType\",\"\"", + "observer.product": "Umbrella", + "observer.type": "dns", + "observer.vendor": "Cisco", + "related.hosts": [ + "some other identity", + "NOERROR" + ], + "related.ip": [ + "192.168.1.1" + ], + "related.user": [ + "elasticuser" + ], + "service.type": "cisco", + "source.address": "some other identity", + "source.domain": "some other identity", + "source.user.name": "elasticuser" + }, + { + "@timestamp": "2020-07-23T23:50:25.000Z", + "cisco.umbrella.blocked_categories": "SomeIdentityType", + "cisco.umbrella.categories": "elastic.co/something.", + "cisco.umbrella.identities": [ + "elasticuser2" + ], + "cisco.umbrella.identity_types": "Test Policy Name", + "cisco.umbrella.policy_identity_type": "Chat,Instant Messaging,Block List,Application", + "destination.address": "192.168.1.1", + "destination.domain": "NOERROR", + "destination.ip": "192.168.1.1", + "dns.question.type": "Blocked", + "dns.response_code": "1 (A)", + "dns.type": "query", + "event.action": "dns-request-4.4.4.4", + "event.category": "network", + "event.dataset": "cisco.umbrella", + "event.module": "cisco", + "event.original": "\\\"2020-07-23 23:50:25\\\",\\\"elasticuser\\\",\\\"elasticuser2\\\",\\\"some other identity\\\",\\\"192.168.1.1\\\",\\\"4.4.4.4\\\",\\\"Blocked\\\",\\\"1 (A)\\\",\\\"NOERROR\\\",\\\"elastic.co/something.\\\",\\\"Chat,Instant Messaging,Block List,Application\\\",\\\"Test Policy Name\\\",\\\"SomeIdentityType\\\",\\\"BlockedCategories\\\"", + "event.type": [ + "connection" + ], + "fileset.name": "umbrella", + "input.type": "log", + "log.offset": 232, + "message": "\"2020-07-23 23:50:25\",\"elasticuser\",\"elasticuser2\",\"some other identity\",\"192.168.1.1\",\"4.4.4.4\",\"Blocked\",\"1 (A)\",\"NOERROR\",\"elastic.co/something.\",\"Chat,Instant Messaging,Block List,Application\",\"Test Policy Name\",\"SomeIdentityType\",\"BlockedCategories\"", + "observer.product": "Umbrella", + "observer.type": "dns", + "observer.vendor": "Cisco", + "related.hosts": [ + "some other identity", + "NOERROR" + ], + "related.ip": [ + "192.168.1.1" + ], + "related.user": [ + "elasticuser" + ], + "service.type": "cisco", + "source.address": "some other identity", + "source.domain": "some other identity", + "source.user.name": "elasticuser" + } +] \ No newline at end of file diff --git a/x-pack/filebeat/module/cisco/umbrella/test/umbrella-iplogs.log b/x-pack/filebeat/module/cisco/umbrella/test/umbrella-iplogs.log new file mode 100644 index 00000000000..6200aeab3ae --- /dev/null +++ b/x-pack/filebeat/module/cisco/umbrella/test/umbrella-iplogs.log @@ -0,0 +1,2 @@ +"2020-08-26 20:32:46","elasticuser","192.168.1.1","0","8.8.8.8","0","Test Category" +"2020-08-26 20:32:45","elasticuser","192.168.1.1","61095","8.8.8.8","445","Test Category" diff --git a/x-pack/filebeat/module/cisco/umbrella/test/umbrella-iplogs.log-expected.json b/x-pack/filebeat/module/cisco/umbrella/test/umbrella-iplogs.log-expected.json new file mode 100644 index 00000000000..4d25464cb61 --- /dev/null +++ b/x-pack/filebeat/module/cisco/umbrella/test/umbrella-iplogs.log-expected.json @@ -0,0 +1,60 @@ +[ + { + "@timestamp": "2020-08-26T20:32:46.000Z", + "cisco.umbrella.categories": "Test Category", + "destination.address": "8.8.8.8", + "destination.ip": "8.8.8.8", + "destination.port": "0", + "event.dataset": "cisco.umbrella", + "event.module": "cisco", + "event.original": "\\\"2020-08-26 20:32:46\\\",\\\"elasticuser\\\",\\\"192.168.1.1\\\",\\\"0\\\",\\\"8.8.8.8\\\",\\\"0\\\",\\\"Test Category\\\"", + "fileset.name": "umbrella", + "input.type": "log", + "log.offset": 0, + "message": "\"2020-08-26 20:32:46\",\"elasticuser\",\"192.168.1.1\",\"0\",\"8.8.8.8\",\"0\",\"Test Category\"", + "observer.product": "Umbrella", + "observer.type": "firewall", + "observer.vendor": "Cisco", + "related.ip": [ + "192.168.1.1", + "8.8.8.8" + ], + "related.user": [ + "elasticuser" + ], + "service.type": "cisco", + "source.address": "192.168.1.1", + "source.ip": "192.168.1.1", + "source.port": "0", + "source.user.name": "elasticuser" + }, + { + "@timestamp": "2020-08-26T20:32:45.000Z", + "cisco.umbrella.categories": "Test Category", + "destination.address": "8.8.8.8", + "destination.ip": "8.8.8.8", + "destination.port": "445", + "event.dataset": "cisco.umbrella", + "event.module": "cisco", + "event.original": "\\\"2020-08-26 20:32:45\\\",\\\"elasticuser\\\",\\\"192.168.1.1\\\",\\\"61095\\\",\\\"8.8.8.8\\\",\\\"445\\\",\\\"Test Category\\\"", + "fileset.name": "umbrella", + "input.type": "log", + "log.offset": 84, + "message": "\"2020-08-26 20:32:45\",\"elasticuser\",\"192.168.1.1\",\"61095\",\"8.8.8.8\",\"445\",\"Test Category\"", + "observer.product": "Umbrella", + "observer.type": "firewall", + "observer.vendor": "Cisco", + "related.ip": [ + "192.168.1.1", + "8.8.8.8" + ], + "related.user": [ + "elasticuser" + ], + "service.type": "cisco", + "source.address": "192.168.1.1", + "source.ip": "192.168.1.1", + "source.port": "61095", + "source.user.name": "elasticuser" + } +] \ No newline at end of file diff --git a/x-pack/filebeat/module/cisco/umbrella/test/umbrella-proxylogs.log b/x-pack/filebeat/module/cisco/umbrella/test/umbrella-proxylogs.log new file mode 100644 index 00000000000..bfe70c6839a --- /dev/null +++ b/x-pack/filebeat/module/cisco/umbrella/test/umbrella-proxylogs.log @@ -0,0 +1,3 @@ +"2020-07-23 23:48:56","elasticuser, someotheruser","192.168.1.1","1.1.1.1","8.8.8.8","","ALLOWED","https://elastic.co/blog/ext_id=Anyclip","https://google.com/elastic","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36","200","850","","","","Business Services","AVDetectionName","Malicious","MalwareName","","","Roaming Computers","" +"2020-07-23 23:48:56","elasticuser, someotheruser","192.168.1.1","1.1.1.1","8.8.8.8","","BLOCKED","https://elastic.co/blog/ext_id=Anyclip","https://google.com/elastic","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36","200","850","","","","Business Services","AVDetectionName","Malicious","MalwareName","","","Roaming Computers","" +"2017-10-02 23:52:53","elasticuser","ActiveDirectoryUserName,ADSite,Network","192.192.192.135","1.1.1.91","","ALLOWED","http://google.com/the.js","www.google.com","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36","200","562","1489","","","","","","","","Networks" diff --git a/x-pack/filebeat/module/cisco/umbrella/test/umbrella-proxylogs.log-expected.json b/x-pack/filebeat/module/cisco/umbrella/test/umbrella-proxylogs.log-expected.json new file mode 100644 index 00000000000..fd474d2d029 --- /dev/null +++ b/x-pack/filebeat/module/cisco/umbrella/test/umbrella-proxylogs.log-expected.json @@ -0,0 +1,115 @@ +[ + { + "@timestamp": "2020-07-23T23:48:56.000Z", + "cisco.umbrella.amp_disposition": "MalwareName", + "cisco.umbrella.av_detections": "AVDetectionName", + "cisco.umbrella.categories": "Business Services", + "cisco.umbrella.identities": "elasticuser, someotheruser", + "cisco.umbrella.identity_types": "Roaming Computers", + "cisco.umbrella.puas": "Malicious", + "destination.address": "8.8.8.8", + "destination.ip": "8.8.8.8", + "event.dataset": "cisco.umbrella", + "event.module": "cisco", + "event.original": "\\\"2020-07-23 23:48:56\\\",\\\"elasticuser, someotheruser\\\",\\\"192.168.1.1\\\",\\\"1.1.1.1\\\",\\\"8.8.8.8\\\",\\\"\\\",\\\"ALLOWED\\\",\\\"https://elastic.co/blog/ext_id=Anyclip\\\",\\\"https://google.com/elastic\\\",\\\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36\\\",\\\"200\\\",\\\"850\\\",\\\"\\\",\\\"\\\",\\\"\\\",\\\"Business Services\\\",\\\"AVDetectionName\\\",\\\"Malicious\\\",\\\"MalwareName\\\",\\\"\\\",\\\"\\\",\\\"Roaming Computers\\\",\\\"\\\"", + "event.type": [ + "allowed" + ], + "fileset.name": "umbrella", + "http.request.bytes": "850", + "http.request.referrer": "https://google.com/elastic", + "http.response.status_code": "200", + "input.type": "log", + "log.offset": 0, + "message": "\"2020-07-23 23:48:56\",\"elasticuser, someotheruser\",\"192.168.1.1\",\"1.1.1.1\",\"8.8.8.8\",\"\",\"ALLOWED\",\"https://elastic.co/blog/ext_id=Anyclip\",\"https://google.com/elastic\",\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36\",\"200\",\"850\",\"\",\"\",\"\",\"Business Services\",\"AVDetectionName\",\"Malicious\",\"MalwareName\",\"\",\"\",\"Roaming Computers\",\"\"", + "observer.product": "Umbrella", + "observer.type": "proxy", + "observer.vendor": "Cisco", + "related.ip": [ + "192.168.1.1", + "1.1.1.1", + "8.8.8.8" + ], + "service.type": "cisco", + "source.address": "192.168.1.1", + "source.ip": "192.168.1.1", + "source.nat.ip": "1.1.1.1", + "url.full": "https://elastic.co/blog/ext_id=Anyclip", + "user_agent.original": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36" + }, + { + "@timestamp": "2020-07-23T23:48:56.000Z", + "cisco.umbrella.amp_disposition": "MalwareName", + "cisco.umbrella.av_detections": "AVDetectionName", + "cisco.umbrella.categories": "Business Services", + "cisco.umbrella.identities": "elasticuser, someotheruser", + "cisco.umbrella.identity_types": "Roaming Computers", + "cisco.umbrella.puas": "Malicious", + "destination.address": "8.8.8.8", + "destination.ip": "8.8.8.8", + "event.dataset": "cisco.umbrella", + "event.module": "cisco", + "event.original": "\\\"2020-07-23 23:48:56\\\",\\\"elasticuser, someotheruser\\\",\\\"192.168.1.1\\\",\\\"1.1.1.1\\\",\\\"8.8.8.8\\\",\\\"\\\",\\\"BLOCKED\\\",\\\"https://elastic.co/blog/ext_id=Anyclip\\\",\\\"https://google.com/elastic\\\",\\\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36\\\",\\\"200\\\",\\\"850\\\",\\\"\\\",\\\"\\\",\\\"\\\",\\\"Business Services\\\",\\\"AVDetectionName\\\",\\\"Malicious\\\",\\\"MalwareName\\\",\\\"\\\",\\\"\\\",\\\"Roaming Computers\\\",\\\"\\\"", + "event.type": [ + "denied" + ], + "fileset.name": "umbrella", + "http.request.bytes": "850", + "http.request.referrer": "https://google.com/elastic", + "http.response.status_code": "200", + "input.type": "log", + "log.offset": 399, + "message": "\"2020-07-23 23:48:56\",\"elasticuser, someotheruser\",\"192.168.1.1\",\"1.1.1.1\",\"8.8.8.8\",\"\",\"BLOCKED\",\"https://elastic.co/blog/ext_id=Anyclip\",\"https://google.com/elastic\",\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36\",\"200\",\"850\",\"\",\"\",\"\",\"Business Services\",\"AVDetectionName\",\"Malicious\",\"MalwareName\",\"\",\"\",\"Roaming Computers\",\"\"", + "observer.product": "Umbrella", + "observer.type": "proxy", + "observer.vendor": "Cisco", + "related.ip": [ + "192.168.1.1", + "1.1.1.1", + "8.8.8.8" + ], + "service.type": "cisco", + "source.address": "192.168.1.1", + "source.ip": "192.168.1.1", + "source.nat.ip": "1.1.1.1", + "url.full": "https://elastic.co/blog/ext_id=Anyclip", + "user_agent.original": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36" + }, + { + "@timestamp": "2017-10-02T23:52:53.000Z", + "cisco.umbrella.amp_score": "Networks", + "cisco.umbrella.identities": "elasticuser", + "destination.address": "1.1.1.91", + "destination.ip": "1.1.1.91", + "event.dataset": "cisco.umbrella", + "event.module": "cisco", + "event.original": "\\\"2017-10-02 23:52:53\\\",\\\"elasticuser\\\",\\\"ActiveDirectoryUserName,ADSite,Network\\\",\\\"192.192.192.135\\\",\\\"1.1.1.91\\\",\\\"\\\",\\\"ALLOWED\\\",\\\"http://google.com/the.js\\\",\\\"www.google.com\\\",\\\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36\\\",\\\"200\\\",\\\"562\\\",\\\"1489\\\",\\\"\\\",\\\"\\\",\\\"\\\",\\\"\\\",\\\"\\\",\\\"\\\",\\\"\\\",\\\"Networks\\\"", + "event.type": [ + "allowed" + ], + "fileset.name": "umbrella", + "http.request.bytes": "562", + "http.request.referrer": "www.google.com", + "http.response.bytes": "1489", + "http.response.status_code": "200", + "input.type": "log", + "log.offset": 798, + "message": "\"2017-10-02 23:52:53\",\"elasticuser\",\"ActiveDirectoryUserName,ADSite,Network\",\"192.192.192.135\",\"1.1.1.91\",\"\",\"ALLOWED\",\"http://google.com/the.js\",\"www.google.com\",\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36\",\"200\",\"562\",\"1489\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"Networks\"", + "observer.product": "Umbrella", + "observer.type": "proxy", + "observer.vendor": "Cisco", + "related.hosts": [ + "ActiveDirectoryUserName,ADSite,Network" + ], + "related.ip": [ + "192.192.192.135", + "1.1.1.91" + ], + "service.type": "cisco", + "source.address": "ActiveDirectoryUserName,ADSite,Network", + "source.domain": "ActiveDirectoryUserName,ADSite,Network", + "source.nat.ip": "192.192.192.135", + "url.full": "http://google.com/the.js", + "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36" + } +] \ No newline at end of file From 84f6311f6d01af786b61667b729ee428d776a015 Mon Sep 17 00:00:00 2001 From: Michal Pristas Date: Mon, 5 Oct 2020 17:33:58 +0200 Subject: [PATCH 20/93] [Ingest Manager] Send updating state (#21461) [Ingest Manager] Send updating state (#21461) --- x-pack/elastic-agent/CHANGELOG.next.asciidoc | 1 + .../pkg/agent/application/managed_mode.go | 4 +- .../pkg/agent/application/upgrade/upgrade.go | 77 +++++++++++++++++-- x-pack/elastic-agent/pkg/core/state/state.go | 2 + x-pack/elastic-agent/pkg/reporter/reporter.go | 6 ++ 5 files changed, 81 insertions(+), 9 deletions(-) diff --git a/x-pack/elastic-agent/CHANGELOG.next.asciidoc b/x-pack/elastic-agent/CHANGELOG.next.asciidoc index 2ba08864ae8..278a9ea9cf4 100644 --- a/x-pack/elastic-agent/CHANGELOG.next.asciidoc +++ b/x-pack/elastic-agent/CHANGELOG.next.asciidoc @@ -28,3 +28,4 @@ - Add support for EQL based condition on inputs {pull}20994[20994] - Send `fleet.host.id` to Endpoint Security {pull}21042[21042] - Add `install` and `uninstall` subcommands {pull}21206[21206] +- Send updating state {pull}21461[21461] diff --git a/x-pack/elastic-agent/pkg/agent/application/managed_mode.go b/x-pack/elastic-agent/pkg/agent/application/managed_mode.go index 12a9c242780..d1eaf197a88 100644 --- a/x-pack/elastic-agent/pkg/agent/application/managed_mode.go +++ b/x-pack/elastic-agent/pkg/agent/application/managed_mode.go @@ -200,11 +200,13 @@ func newManaged( } managedApplication.upgrader = upgrade.NewUpgrader( + agentInfo, cfg.Settings.DownloadConfig, log, []context.CancelFunc{managedApplication.cancelCtxFn}, reexec, - acker) + acker, + combinedReporter) actionDispatcher.MustRegister( &fleetapi.ActionPolicyChange{}, diff --git a/x-pack/elastic-agent/pkg/agent/application/upgrade/upgrade.go b/x-pack/elastic-agent/pkg/agent/application/upgrade/upgrade.go index cac36ef7922..7aacf77ba63 100644 --- a/x-pack/elastic-agent/pkg/agent/application/upgrade/upgrade.go +++ b/x-pack/elastic-agent/pkg/agent/application/upgrade/upgrade.go @@ -20,6 +20,7 @@ import ( "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/install" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/state" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/fleetapi" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/release" ) @@ -33,11 +34,13 @@ const ( // Upgrader performs an upgrade type Upgrader struct { + agentInfo *info.AgentInfo settings *artifact.Config log *logger.Logger closers []context.CancelFunc reexec reexecManager acker acker + reporter stateReporter upgradeable bool } @@ -50,14 +53,19 @@ type acker interface { Commit(ctx context.Context) error } +type stateReporter interface { + OnStateChange(id string, name string, s state.State) +} + // NewUpgrader creates an upgrader which is capable of performing upgrade operation -func NewUpgrader(settings *artifact.Config, log *logger.Logger, closers []context.CancelFunc, reexec reexecManager, a acker) *Upgrader { +func NewUpgrader(agentInfo *info.AgentInfo, settings *artifact.Config, log *logger.Logger, closers []context.CancelFunc, reexec reexecManager, a acker, r stateReporter) *Upgrader { return &Upgrader{ settings: settings, log: log, closers: closers, reexec: reexec, acker: a, + reporter: r, upgradeable: getUpgradable(), } } @@ -68,13 +76,22 @@ func (u *Upgrader) Upgradeable() bool { } // Upgrade upgrades running agent -func (u *Upgrader) Upgrade(ctx context.Context, a *fleetapi.ActionUpgrade) error { +func (u *Upgrader) Upgrade(ctx context.Context, a *fleetapi.ActionUpgrade) (err error) { + // report failed + defer func() { + if err != nil { + u.reportFailure(ctx, a, err) + } + }() + if !u.upgradeable { return fmt.Errorf( "cannot be upgraded; must be installed with install sub-command and " + "running under control of the systems supervisor") } + u.reportUpdating(a.Version) + sourceURI, err := u.sourceURI(a.Version, a.SourceURI) archivePath, err := u.downloadArtifact(ctx, a.Version, sourceURI) if err != nil { @@ -91,7 +108,10 @@ func (u *Upgrader) Upgrade(ctx context.Context, a *fleetapi.ActionUpgrade) error } if strings.HasPrefix(release.Commit(), newHash) { - return errors.New("upgrading to same version") + // not an error + u.ackAction(ctx, a) + u.log.Warn("upgrading to same version") + return nil } if err := copyActionStore(newHash); err != nil { @@ -132,11 +152,7 @@ func (u *Upgrader) Ack(ctx context.Context) error { return nil } - if err := u.acker.Ack(ctx, marker.Action); err != nil { - return err - } - - if err := u.acker.Commit(ctx); err != nil { + if err := u.ackAction(ctx, marker.Action); err != nil { return err } @@ -148,6 +164,7 @@ func (u *Upgrader) Ack(ctx context.Context) error { return ioutil.WriteFile(markerFile, markerBytes, 0600) } + func (u *Upgrader) sourceURI(version, retrievedURI string) (string, error) { if strings.HasSuffix(version, "-SNAPSHOT") && retrievedURI == "" { return "", errors.New("snapshot upgrade requires source uri", errors.TypeConfig) @@ -159,6 +176,50 @@ func (u *Upgrader) sourceURI(version, retrievedURI string) (string, error) { return u.settings.SourceURI, nil } +// ackAction is used for successful updates, it was either updated successfully or to the same version +// so we need to remove updating state and get prevent from receiving same update action again. +func (u *Upgrader) ackAction(ctx context.Context, action fleetapi.Action) error { + if err := u.acker.Ack(ctx, action); err != nil { + return err + } + + if err := u.acker.Commit(ctx); err != nil { + return err + } + + u.reporter.OnStateChange( + "", + agentName, + state.State{Status: state.Running}, + ) + + return nil +} + +// report failure is used when update process fails. action is acked so it won't be received again +// and state is changed to FAILED +func (u *Upgrader) reportFailure(ctx context.Context, action fleetapi.Action, err error) { + // ack action + u.acker.Ack(ctx, action) + + // report failure + u.reporter.OnStateChange( + "", + agentName, + state.State{Status: state.Failed, Message: err.Error()}, + ) +} + +// reportUpdating sets state of agent to updating. +func (u *Upgrader) reportUpdating(version string) { + // report failure + u.reporter.OnStateChange( + "", + agentName, + state.State{Status: state.Updating, Message: fmt.Sprintf("Update to version '%s' started", version)}, + ) +} + func rollbackInstall(hash string) { os.RemoveAll(filepath.Join(paths.Data(), fmt.Sprintf("%s-%s", agentName, hash))) } diff --git a/x-pack/elastic-agent/pkg/core/state/state.go b/x-pack/elastic-agent/pkg/core/state/state.go index 6b7c8bd53de..670cdc2a2f2 100644 --- a/x-pack/elastic-agent/pkg/core/state/state.go +++ b/x-pack/elastic-agent/pkg/core/state/state.go @@ -30,6 +30,8 @@ const ( Crashed // Restarting is status describing application is restarting. Restarting + // Updating is status describing application is updating. + Updating ) // State wraps the process state and application status. diff --git a/x-pack/elastic-agent/pkg/reporter/reporter.go b/x-pack/elastic-agent/pkg/reporter/reporter.go index c36708a837f..3b128841b2a 100644 --- a/x-pack/elastic-agent/pkg/reporter/reporter.go +++ b/x-pack/elastic-agent/pkg/reporter/reporter.go @@ -38,6 +38,8 @@ const ( EventSubTypeFailed = "FAILED" // EventSubTypeStopping is an event type indicating application is stopping. EventSubTypeStopping = "STOPPING" + // EventSubTypeUpdating is an event type indicating update process in progress. + EventSubTypeUpdating = "UPDATING" ) type agentInfo interface { @@ -127,6 +129,10 @@ func generateRecord(agentID string, id string, name string, s state.State) event case state.Restarting: subType = EventSubTypeStarting subTypeText = "RESTARTING" + case state.Updating: + subType = EventSubTypeUpdating + subTypeText = EventSubTypeUpdating + } err := errors.New( From 931e3cafe1beb6b99d0706d51d25f422f59ea260 Mon Sep 17 00:00:00 2001 From: kaiyan-sheng Date: Mon, 5 Oct 2020 10:00:07 -0600 Subject: [PATCH 21/93] Fix billing.go aws.GetStartTimeEndTime (#21531) This PR is to fix this error introduced in https://github.com/elastic/beats/pull/20875: ``` # github.com/elastic/beats/v7/x-pack/metricbeat/module/aws/billing ../x-pack/metricbeat/module/aws/billing/billing.go:119:47: not enough arguments in call to "github.com/elastic/beats/v7/x-pack/metricbeat/module/aws".GetStartTimeEndTime have (time.Duration) want (time.Duration, time.Duration) Error: error getting default metricsets: Error running subcommand to get metricsets: running "go run /home/travis/gopath/src/github.com/elastic/beats/x-pack/metricbeat/scripts/msetlists/main.go" failed with exit code 2 ``` --- x-pack/metricbeat/module/aws/billing/billing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/metricbeat/module/aws/billing/billing.go b/x-pack/metricbeat/module/aws/billing/billing.go index 2eb2bd2854a..b9b971e3d34 100644 --- a/x-pack/metricbeat/module/aws/billing/billing.go +++ b/x-pack/metricbeat/module/aws/billing/billing.go @@ -116,7 +116,7 @@ func (m *MetricSet) Fetch(report mb.ReporterV2) error { startDate, endDate := getStartDateEndDate(m.Period) // Get startTime and endTime - startTime, endTime := aws.GetStartTimeEndTime(m.Period) + startTime, endTime := aws.GetStartTimeEndTime(m.Period, m.Latency) // get cost metrics from cost explorer awsConfig := m.MetricSet.AwsConfig.Copy() From 527ce19ca8fce4ff4dff6b04303768f460f72598 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Mon, 5 Oct 2020 17:03:02 +0100 Subject: [PATCH 22/93] [CI] fix 'no matches found within 10000' (#21466) --- Jenkinsfile | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 317a5c781e3..08853ab1453 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -74,7 +74,7 @@ pipeline { } steps { withGithubNotify(context: 'Lint') { - withBeatsEnv(archive: true) { + withBeatsEnv(archive: true, id: 'lint') { dumpVariables() cmd(label: 'make check', script: 'make check') } @@ -345,8 +345,13 @@ def archiveTestOutput(Map args = [:]) { } cmd(label: 'Prepare test output', script: 'python .ci/scripts/pre_archive_test.py') dir('build') { + if (isUnix()) { + cmd(label: 'Delete folders that are causing exceptions (See JENKINS-58421)', + returnStatus: true, + script: 'rm -rf ve || true; find . -type d -name vendor -exec rm -r {} \\;') + } else { log(level: 'INFO', text: 'Delete folders that are causing exceptions (See JENKINS-58421) is disabled for Windows.') } junitAndStore(allowEmptyResults: true, keepLongStdio: true, testResults: args.testResults, stashedTestReports: stashedTestReports, id: args.id) - archiveArtifacts(allowEmptyArchive: true, artifacts: args.artifacts) + tar(file: "test-build-artifacts-${args.id}.tgz", dir: '.', archive: true, allowMissing: true) } catchError(buildResult: 'SUCCESS', message: 'Failed to archive the build test results', stageResult: 'SUCCESS') { def folder = cmd(label: 'Find system-tests', returnStdout: true, script: 'python .ci/scripts/search_system_tests.py').trim() From 40b24dd2d00bb2a83b46449e452f4b7f86d59087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9mi=20V=C3=A1nyi?= Date: Mon, 5 Oct 2020 18:24:42 +0200 Subject: [PATCH 23/93] Add filestream input reader (#21481) ## What does this PR do? This PR adds the event reader and publisher functionality. This is mostly the refactoring of `Harvester` from `filebeat/input/log`. Two things are missing: metrics and special readers e.g. `multiline`. --- filebeat/input/filestream/config.go | 4 +- filebeat/input/filestream/filestream.go | 75 +++++- filebeat/input/filestream/input.go | 307 +++++++++++++++++++++++- 3 files changed, 365 insertions(+), 21 deletions(-) diff --git a/filebeat/input/filestream/config.go b/filebeat/input/filestream/config.go index 93b23232594..3ec076196f0 100644 --- a/filebeat/input/filestream/config.go +++ b/filebeat/input/filestream/config.go @@ -48,12 +48,12 @@ type closerConfig struct { type readerCloserConfig struct { AfterInterval time.Duration - Inactive time.Duration OnEOF bool } type stateChangeCloserConfig struct { CheckInterval time.Duration + Inactive time.Duration Removed bool Renamed bool } @@ -94,11 +94,11 @@ func defaultCloserConfig() closerConfig { OnStateChange: stateChangeCloserConfig{ CheckInterval: 5 * time.Second, Removed: true, // TODO check clean_removed option + Inactive: 0 * time.Second, Renamed: false, }, Reader: readerCloserConfig{ OnEOF: false, - Inactive: 0 * time.Second, AfterInterval: 0 * time.Second, }, } diff --git a/filebeat/input/filestream/filestream.go b/filebeat/input/filestream/filestream.go index 59f26ccca1b..4d42bbf6242 100644 --- a/filebeat/input/filestream/filestream.go +++ b/filebeat/input/filestream/filestream.go @@ -26,6 +26,7 @@ import ( input "github.com/elastic/beats/v7/filebeat/input/v2" "github.com/elastic/beats/v7/libbeat/common/backoff" + "github.com/elastic/beats/v7/libbeat/common/file" "github.com/elastic/beats/v7/libbeat/logp" "github.com/elastic/go-concert/ctxtool" "github.com/elastic/go-concert/unison" @@ -43,10 +44,14 @@ type logFile struct { ctx context.Context cancelReading context.CancelFunc - closeInactive time.Duration closeAfterInterval time.Duration closeOnEOF bool + checkInterval time.Duration + closeInactive time.Duration + closeRemoved bool + closeRenamed bool + offset int64 lastTimeRead time.Time backoff backoff.Backoff @@ -59,7 +64,7 @@ func newFileReader( canceler input.Canceler, f *os.File, config readerConfig, - closerConfig readerCloserConfig, + closerConfig closerConfig, ) (*logFile, error) { offset, err := f.Seek(0, os.SEEK_CUR) if err != nil { @@ -69,9 +74,12 @@ func newFileReader( l := &logFile{ file: f, log: log, - closeInactive: closerConfig.Inactive, - closeAfterInterval: closerConfig.AfterInterval, - closeOnEOF: closerConfig.OnEOF, + closeAfterInterval: closerConfig.Reader.AfterInterval, + closeOnEOF: closerConfig.Reader.OnEOF, + checkInterval: closerConfig.OnStateChange.CheckInterval, + closeInactive: closerConfig.OnStateChange.Inactive, + closeRemoved: closerConfig.OnStateChange.Removed, + closeRenamed: closerConfig.OnStateChange.Renamed, offset: offset, lastTimeRead: time.Now(), backoff: backoff.NewExpBackoff(canceler.Done(), config.Backoff.Init, config.Backoff.Max), @@ -143,7 +151,7 @@ func (f *logFile) startFileMonitoringIfNeeded() { if f.closeAfterInterval > 0 { f.tg.Go(func(ctx unison.Canceler) error { - f.closeIfInactive(ctx) + f.periodicStateCheck(ctx) return nil }) } @@ -164,10 +172,8 @@ func (f *logFile) closeIfTimeout(ctx unison.Canceler) { } } -func (f *logFile) closeIfInactive(ctx unison.Canceler) { - // This can be made configureble if users need a more flexible - // cheking for inactive files. - ticker := time.NewTicker(5 * time.Minute) +func (f *logFile) periodicStateCheck(ctx unison.Canceler) { + ticker := time.NewTicker(f.checkInterval) defer ticker.Stop() for { @@ -175,8 +181,7 @@ func (f *logFile) closeIfInactive(ctx unison.Canceler) { case <-ctx.Done(): return case <-ticker.C: - age := time.Since(f.lastTimeRead) - if age > f.closeInactive { + if f.shouldBeClosed() { f.cancelReading() return } @@ -184,6 +189,52 @@ func (f *logFile) closeIfInactive(ctx unison.Canceler) { } } +func (f *logFile) shouldBeClosed() bool { + if f.closeInactive > 0 { + if time.Since(f.lastTimeRead) > f.closeInactive { + return true + } + } + + if !f.closeRemoved && !f.closeRenamed { + return false + + } + + info, statErr := f.file.Stat() + if statErr != nil { + f.log.Errorf("Unexpected error reading from %s; error: %s", f.file.Name(), statErr) + return true + } + + if f.closeRenamed { + // Check if the file can still be found under the same path + if !isSameFile(f.file.Name(), info) { + f.log.Debugf("close_renamed is enabled and file %s has been renamed", f.file.Name()) + return true + } + } + + if f.closeRemoved { + // Check if the file name exists. See https://github.com/elastic/filebeat/issues/93 + if file.IsRemoved(f.file) { + f.log.Debugf("close_removed is enabled and file %s has been removed", f.file.Name()) + return true + } + } + + return false +} + +func isSameFile(path string, info os.FileInfo) bool { + fileInfo, err := os.Stat(path) + if err != nil { + return false + } + + return os.SameFile(fileInfo, info) +} + // errorChecks determines the cause for EOF errors, and how the EOF event should be handled // based on the config options. func (f *logFile) errorChecks(err error) error { diff --git a/filebeat/input/filestream/input.go b/filebeat/input/filestream/input.go index bcd143c1c5a..b6c6598c50b 100644 --- a/filebeat/input/filestream/input.go +++ b/filebeat/input/filestream/input.go @@ -18,16 +18,27 @@ package filestream import ( + "fmt" + "os" + + "golang.org/x/text/transform" + + "github.com/elastic/go-concert/ctxtool" + loginp "github.com/elastic/beats/v7/filebeat/input/filestream/internal/input-logfile" input "github.com/elastic/beats/v7/filebeat/input/v2" + "github.com/elastic/beats/v7/libbeat/beat" "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/common/match" "github.com/elastic/beats/v7/libbeat/feature" "github.com/elastic/beats/v7/libbeat/logp" + "github.com/elastic/beats/v7/libbeat/reader" + "github.com/elastic/beats/v7/libbeat/reader/debug" + "github.com/elastic/beats/v7/libbeat/reader/readfile" + "github.com/elastic/beats/v7/libbeat/reader/readfile/encoding" ) -// filestream is the input for reading from files which -// are actively written by other applications. -type filestream struct{} +const pluginName = "filestream" type state struct { Source string `json:"source" struct:"source"` @@ -35,7 +46,20 @@ type state struct { IdentifierName string `json:"identifier_name" struct:"identifier_name"` } -const pluginName = "filestream" +// filestream is the input for reading from files which +// are actively written by other applications. +type filestream struct { + readerConfig readerConfig + bufferSize int + tailFile bool // TODO + encodingFactory encoding.EncodingFactory + encoding encoding.Encoding + lineTerminator readfile.LineTerminator + excludeLines []match.Matcher + includeLines []match.Matcher + maxBytes int + closerConfig closerConfig +} // Plugin creates a new filestream input plugin for creating a stateful input. func Plugin(log *logp.Logger, store loginp.StateStore) input.Plugin { @@ -55,13 +79,46 @@ func Plugin(log *logp.Logger, store loginp.StateStore) input.Plugin { } func configure(cfg *common.Config) (loginp.Prospector, loginp.Harvester, error) { - panic("TODO: implement me") + config := defaultConfig() + if err := cfg.Unpack(&config); err != nil { + return nil, nil, err + } + + prospector, err := newFileProspector( + config.Paths, + config.IgnoreOlder, + config.FileWatcher, + config.FileIdentity, + ) + if err != nil { + return nil, nil, err + } + + encodingFactory, ok := encoding.FindEncoding(config.Reader.Encoding) + if !ok || encodingFactory == nil { + return nil, nil, fmt.Errorf("unknown encoding('%v')", config.Reader.Encoding) + } + + return prospector, &filestream{ + readerConfig: config.Reader, + bufferSize: config.Reader.BufferSize, + encodingFactory: encodingFactory, + lineTerminator: config.Reader.LineTerminator, + excludeLines: config.Reader.ExcludeLines, + includeLines: config.Reader.IncludeLines, + maxBytes: config.Reader.MaxBytes, + closerConfig: config.Close, + }, nil } func (inp *filestream) Name() string { return pluginName } func (inp *filestream) Test(src loginp.Source, ctx input.TestContext) error { - panic("TODO: implement me") + reader, err := inp.open(ctx.Logger, ctx.Cancelation, state{}) + if err != nil { + return err + } + return reader.Close() } func (inp *filestream) Run( @@ -70,5 +127,241 @@ func (inp *filestream) Run( cursor loginp.Cursor, publisher loginp.Publisher, ) error { - panic("TODO: implement me") + fs, ok := src.(fileSource) + if !ok { + return fmt.Errorf("not file source") + } + + log := ctx.Logger.With("path", fs.newPath).With("state-id", src.Name()) + state := initState(log, cursor, fs) + + r, err := inp.open(log, ctx.Cancelation, state) + if err != nil { + log.Errorf("File could not be opened for reading: %v", err) + return err + } + + _, streamCancel := ctxtool.WithFunc(ctxtool.FromCanceller(ctx.Cancelation), func() { + log.Debug("Closing reader of filestream") + err := r.Close() + if err != nil { + log.Errorf("Error stopping filestream reader %v", err) + } + }) + defer streamCancel() + + return inp.readFromSource(ctx, log, r, fs.newPath, state, publisher) +} + +func initState(log *logp.Logger, c loginp.Cursor, s fileSource) state { + state := state{Source: s.newPath, IdentifierName: s.identifierGenerator} + if c.IsNew() { + return state + } + + err := c.Unpack(&state) + if err != nil { + log.Error("Cannot serialize cursor data into file state: %+v", err) + } + + return state +} + +func (inp *filestream) open(log *logp.Logger, canceler input.Canceler, s state) (reader.Reader, error) { + f, err := inp.openFile(s.Source, s.Offset) + if err != nil { + return nil, err + } + + log.Debug("newLogFileReader with config.MaxBytes:", inp.maxBytes) + + // TODO: NewLineReader uses additional buffering to deal with encoding and testing + // for new lines in input stream. Simple 8-bit based encodings, or plain + // don't require 'complicated' logic. + logReader, err := newFileReader(log, canceler, f, inp.readerConfig, inp.closerConfig) + if err != nil { + return nil, err + } + + dbgReader, err := debug.AppendReaders(logReader) + if err != nil { + f.Close() + return nil, err + } + + // Configure MaxBytes limit for EncodeReader as multiplied by 4 + // for the worst case scenario where incoming UTF32 charchers are decoded to the single byte UTF-8 characters. + // This limit serves primarily to avoid memory bload or potential OOM with expectedly long lines in the file. + // The further size limiting is performed by LimitReader at the end of the readers pipeline as needed. + encReaderMaxBytes := inp.maxBytes * 4 + + var r reader.Reader + r, err = readfile.NewEncodeReader(dbgReader, readfile.Config{ + Codec: inp.encoding, + BufferSize: inp.bufferSize, + Terminator: inp.lineTerminator, + MaxBytes: encReaderMaxBytes, + }) + if err != nil { + f.Close() + return nil, err + } + + r = readfile.NewStripNewline(r, inp.lineTerminator) + r = readfile.NewLimitReader(r, inp.maxBytes) + + return r, nil +} + +// openFile opens a file and checks for the encoding. In case the encoding cannot be detected +// or the file cannot be opened because for example of failing read permissions, an error +// is returned and the harvester is closed. The file will be picked up again the next time +// the file system is scanned +func (inp *filestream) openFile(path string, offset int64) (*os.File, error) { + err := inp.checkFileBeforeOpening(path) + if err != nil { + return nil, err + } + + f, err := os.OpenFile(path, os.O_RDONLY, os.FileMode(0)) + if err != nil { + return nil, fmt.Errorf("failed opening %s: %s", path, err) + } + + err = inp.initFileOffset(f, offset) + if err != nil { + f.Close() + return nil, err + } + + inp.encoding, err = inp.encodingFactory(f) + if err != nil { + f.Close() + if err == transform.ErrShortSrc { + return nil, fmt.Errorf("initialising encoding for '%v' failed due to file being too short", f) + } + return nil, fmt.Errorf("initialising encoding for '%v' failed: %v", f, err) + } + + return f, nil +} + +func (inp *filestream) checkFileBeforeOpening(path string) error { + fi, err := os.Stat(path) + if err != nil { + return fmt.Errorf("failed to stat source file %s: %v", path, err) + } + + if !fi.Mode().IsRegular() { + return fmt.Errorf("tried to open non regular file: %q %s", fi.Mode(), fi.Name()) + } + + if fi.Mode()&os.ModeNamedPipe != 0 { + return fmt.Errorf("failed to open file %s, named pipes are not supported", path) + } + + return nil +} + +func (inp *filestream) initFileOffset(file *os.File, offset int64) error { + if offset > 0 { + _, err := file.Seek(offset, os.SEEK_SET) + return err + } + + // get offset from file in case of encoding factory was required to read some data. + _, err := file.Seek(0, os.SEEK_CUR) + return err +} + +func (inp *filestream) readFromSource( + ctx input.Context, + log *logp.Logger, + r reader.Reader, + path string, + s state, + p loginp.Publisher, +) error { + for ctx.Cancelation.Err() == nil { + message, err := r.Next() + if err != nil { + switch err { + case ErrFileTruncate: + log.Info("File was truncated. Begin reading file from offset 0.") + s.Offset = 0 + case ErrClosed: + log.Info("Reader was closed. Closing.") + case reader.ErrLineUnparsable: + log.Info("Skipping unparsable line in file.") + continue + default: + log.Errorf("Read line error: %v", err) + } + return nil + } + + if message.IsEmpty() || inp.isDroppedLine(log, string(message.Content)) { + continue + } + + event := inp.eventFromMessage(message, path) + s.Offset += int64(message.Bytes) + + if err := p.Publish(event, s); err != nil { + return err + } + } + return nil +} + +// isDroppedLine decides if the line is exported or not based on +// the include_lines and exclude_lines options. +func (inp *filestream) isDroppedLine(log *logp.Logger, line string) bool { + if len(inp.includeLines) > 0 { + if !matchAny(inp.includeLines, line) { + log.Debug("Drop line as it does not match any of the include patterns %s", line) + return true + } + } + if len(inp.excludeLines) > 0 { + if matchAny(inp.excludeLines, line) { + log.Debug("Drop line as it does match one of the exclude patterns%s", line) + return true + } + } + + return false +} + +func matchAny(matchers []match.Matcher, text string) bool { + for _, m := range matchers { + if m.MatchString(text) { + return true + } + } + return false +} + +func (inp *filestream) eventFromMessage(m reader.Message, path string) beat.Event { + fields := common.MapStr{ + "log": common.MapStr{ + "offset": m.Bytes, // Offset here is the offset before the starting char. + "file": common.MapStr{ + "path": path, + }, + }, + } + fields.DeepUpdate(m.Fields) + + if len(m.Content) > 0 { + if fields == nil { + fields = common.MapStr{} + } + fields["message"] = string(m.Content) + } + + return beat.Event{ + Timestamp: m.Ts, + Fields: fields, + } } From f4ebcf0dfc1a64bdd3ab5e2b70fa49bb4c1517b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9mi=20V=C3=A1nyi?= Date: Mon, 5 Oct 2020 19:18:35 +0200 Subject: [PATCH 24/93] Enable filestream input (#21533) The feature is enabled, but it is not yet documented. --- filebeat/input/default-inputs/inputs.go | 6 ++++-- filebeat/input/filestream/fswatch.go | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/filebeat/input/default-inputs/inputs.go b/filebeat/input/default-inputs/inputs.go index 52338a0af98..881f3664efd 100644 --- a/filebeat/input/default-inputs/inputs.go +++ b/filebeat/input/default-inputs/inputs.go @@ -19,6 +19,7 @@ package inputs import ( "github.com/elastic/beats/v7/filebeat/beater" + "github.com/elastic/beats/v7/filebeat/input/filestream" "github.com/elastic/beats/v7/filebeat/input/unix" v2 "github.com/elastic/beats/v7/filebeat/input/v2" "github.com/elastic/beats/v7/libbeat/beat" @@ -27,13 +28,14 @@ import ( func Init(info beat.Info, log *logp.Logger, components beater.StateStore) []v2.Plugin { return append( - genericInputs(), + genericInputs(log, components), osInputs(info, log, components)..., ) } -func genericInputs() []v2.Plugin { +func genericInputs(log *logp.Logger, components beater.StateStore) []v2.Plugin { return []v2.Plugin{ + filestream.Plugin(log, components), unix.Plugin(), } } diff --git a/filebeat/input/filestream/fswatch.go b/filebeat/input/filestream/fswatch.go index d4bc1b5ea08..1b80971d835 100644 --- a/filebeat/input/filestream/fswatch.go +++ b/filebeat/input/filestream/fswatch.go @@ -74,7 +74,7 @@ type fileWatcher struct { func newFileWatcher(paths []string, ns *common.ConfigNamespace) (loginp.FSWatcher, error) { if ns == nil { - return newScannerWatcher(paths, nil) + return newScannerWatcher(paths, common.NewConfig()) } watcherType := ns.Name() From c858dd0f3188793ffd52d8975c8120ce0f442ff8 Mon Sep 17 00:00:00 2001 From: Blake Rouse Date: Mon, 5 Oct 2020 14:38:56 -0400 Subject: [PATCH 25/93] [Elastic Agent] Add upgrade CLI to initiate upgrade of Agent locally (#21425) * Add new upgrade command to initiate a local upgrade of Elastic Agent. * Update drop path with file:// prefix is defined. * Add comment. * Add missing new line. * Add changelog. * Prevent upgrading of Agent locally when connected to Fleet. * Fixes from rebase. --- x-pack/elastic-agent/CHANGELOG.next.asciidoc | 1 + .../pkg/agent/application/application.go | 12 +++- .../application/handler_action_upgrade.go | 18 ++++- .../pkg/agent/application/local_mode.go | 14 ++++ .../application/upgrade/step_download.go | 9 ++- .../agent/application/upgrade/step_mark.go | 4 +- .../pkg/agent/application/upgrade/upgrade.go | 37 ++++++++--- x-pack/elastic-agent/pkg/agent/cmd/common.go | 1 + x-pack/elastic-agent/pkg/agent/cmd/run.go | 4 +- x-pack/elastic-agent/pkg/agent/cmd/upgrade.go | 56 ++++++++++++++++ .../pkg/agent/control/control_test.go | 2 +- .../pkg/agent/control/server/server.go | 65 +++++++++++++++++-- .../pkg/basecmd/version/cmd_test.go | 4 +- 13 files changed, 198 insertions(+), 29 deletions(-) create mode 100644 x-pack/elastic-agent/pkg/agent/cmd/upgrade.go diff --git a/x-pack/elastic-agent/CHANGELOG.next.asciidoc b/x-pack/elastic-agent/CHANGELOG.next.asciidoc index 278a9ea9cf4..7d6870328c7 100644 --- a/x-pack/elastic-agent/CHANGELOG.next.asciidoc +++ b/x-pack/elastic-agent/CHANGELOG.next.asciidoc @@ -29,3 +29,4 @@ - Send `fleet.host.id` to Endpoint Security {pull}21042[21042] - Add `install` and `uninstall` subcommands {pull}21206[21206] - Send updating state {pull}21461[21461] +- Add `upgrade` subcommand to perform upgrade of installed Elastic Agent {pull}21425[21425] diff --git a/x-pack/elastic-agent/pkg/agent/application/application.go b/x-pack/elastic-agent/pkg/agent/application/application.go index e003eed61a6..d721a8aa148 100644 --- a/x-pack/elastic-agent/pkg/agent/application/application.go +++ b/x-pack/elastic-agent/pkg/agent/application/application.go @@ -8,6 +8,7 @@ import ( "context" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/info" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/upgrade" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/configuration" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/warn" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/config" @@ -25,8 +26,12 @@ type reexecManager interface { ReExec(argOverrides ...string) } +type upgraderControl interface { + SetUpgrader(upgrader *upgrade.Upgrader) +} + // New creates a new Agent and bootstrap the required subsystem. -func New(log *logger.Logger, pathConfigFile string, reexec reexecManager) (Application, error) { +func New(log *logger.Logger, pathConfigFile string, reexec reexecManager, uc upgraderControl) (Application, error) { // Load configuration from disk to understand in which mode of operation // we must start the elastic-agent, the mode of operation cannot be changed without restarting the // elastic-agent. @@ -39,7 +44,7 @@ func New(log *logger.Logger, pathConfigFile string, reexec reexecManager) (Appli return nil, err } - return createApplication(log, pathConfigFile, rawConfig, reexec) + return createApplication(log, pathConfigFile, rawConfig, reexec, uc) } func createApplication( @@ -47,6 +52,7 @@ func createApplication( pathConfigFile string, rawConfig *config.Config, reexec reexecManager, + uc upgraderControl, ) (Application, error) { warn.LogNotGA(log) log.Info("Detecting execution mode") @@ -59,7 +65,7 @@ func createApplication( if isStandalone(cfg.Fleet) { log.Info("Agent is managed locally") - return newLocal(ctx, log, pathConfigFile, rawConfig) + return newLocal(ctx, log, pathConfigFile, rawConfig, reexec, uc) } log.Info("Agent is managed by Fleet") diff --git a/x-pack/elastic-agent/pkg/agent/application/handler_action_upgrade.go b/x-pack/elastic-agent/pkg/agent/application/handler_action_upgrade.go index 4d0026d4d79..a4940cfe55b 100644 --- a/x-pack/elastic-agent/pkg/agent/application/handler_action_upgrade.go +++ b/x-pack/elastic-agent/pkg/agent/application/handler_action_upgrade.go @@ -27,5 +27,21 @@ func (h *handlerUpgrade) Handle(ctx context.Context, a action, acker fleetAcker) return fmt.Errorf("invalid type, expected ActionUpgrade and received %T", a) } - return h.upgrader.Upgrade(ctx, action) + return h.upgrader.Upgrade(ctx, &upgradeAction{action}, true) +} + +type upgradeAction struct { + *fleetapi.ActionUpgrade +} + +func (a *upgradeAction) Version() string { + return a.ActionUpgrade.Version +} + +func (a *upgradeAction) SourceURI() string { + return a.ActionUpgrade.SourceURI +} + +func (a *upgradeAction) FleetAction() *fleetapi.ActionUpgrade { + return a.ActionUpgrade } diff --git a/x-pack/elastic-agent/pkg/agent/application/local_mode.go b/x-pack/elastic-agent/pkg/agent/application/local_mode.go index 5559089404e..f8eed0f5792 100644 --- a/x-pack/elastic-agent/pkg/agent/application/local_mode.go +++ b/x-pack/elastic-agent/pkg/agent/application/local_mode.go @@ -9,6 +9,7 @@ import ( "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/filters" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/info" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/upgrade" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/configrequest" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/configuration" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" @@ -60,6 +61,8 @@ func newLocal( log *logger.Logger, pathConfigFile string, rawConfig *config.Config, + reexec reexecManager, + uc upgraderControl, ) (*Local, error) { cfg, err := configuration.NewFromConfig(rawConfig) if err != nil { @@ -135,6 +138,17 @@ func newLocal( localApplication.source = cfgSource + // create a upgrader to use in local mode + upgrader := upgrade.NewUpgrader( + agentInfo, + cfg.Settings.DownloadConfig, + log, + []context.CancelFunc{localApplication.cancelCtxFn}, + reexec, + newNoopAcker(), + reporter) + uc.SetUpgrader(upgrader) + return localApplication, nil } diff --git a/x-pack/elastic-agent/pkg/agent/application/upgrade/step_download.go b/x-pack/elastic-agent/pkg/agent/application/upgrade/step_download.go index 9db442d3655..cf3a3656724 100644 --- a/x-pack/elastic-agent/pkg/agent/application/upgrade/step_download.go +++ b/x-pack/elastic-agent/pkg/agent/application/upgrade/step_download.go @@ -6,6 +6,7 @@ package upgrade import ( "context" + "strings" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" downloader "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact/download/localremote" @@ -16,7 +17,13 @@ func (u *Upgrader) downloadArtifact(ctx context.Context, version, sourceURI stri // do not update source config settings := *u.settings if sourceURI != "" { - settings.SourceURI = sourceURI + if strings.HasPrefix(sourceURI, "file://") { + // update the DropPath so the fs.Downloader can download from this + // path instead of looking into the installed downloads directory + settings.DropPath = strings.TrimPrefix(sourceURI, "file://") + } else { + settings.SourceURI = sourceURI + } } allowEmptyPgp, pgp := release.PGP() diff --git a/x-pack/elastic-agent/pkg/agent/application/upgrade/step_mark.go b/x-pack/elastic-agent/pkg/agent/application/upgrade/step_mark.go index 53920e6ecff..8d03fec3ff6 100644 --- a/x-pack/elastic-agent/pkg/agent/application/upgrade/step_mark.go +++ b/x-pack/elastic-agent/pkg/agent/application/upgrade/step_mark.go @@ -37,7 +37,7 @@ type updateMarker struct { } // markUpgrade marks update happened so we can handle grace period -func (h *Upgrader) markUpgrade(ctx context.Context, hash string, action *fleetapi.ActionUpgrade) error { +func (h *Upgrader) markUpgrade(ctx context.Context, hash string, action Action) error { prevVersion := release.Version() prevHash := release.Commit() if len(prevHash) > hashLen { @@ -49,7 +49,7 @@ func (h *Upgrader) markUpgrade(ctx context.Context, hash string, action *fleetap UpdatedOn: time.Now(), PrevVersion: prevVersion, PrevHash: prevHash, - Action: action, + Action: action.FleetAction(), } markerBytes, err := yaml.Marshal(marker) diff --git a/x-pack/elastic-agent/pkg/agent/application/upgrade/upgrade.go b/x-pack/elastic-agent/pkg/agent/application/upgrade/upgrade.go index 7aacf77ba63..1a21bc154a1 100644 --- a/x-pack/elastic-agent/pkg/agent/application/upgrade/upgrade.go +++ b/x-pack/elastic-agent/pkg/agent/application/upgrade/upgrade.go @@ -44,6 +44,16 @@ type Upgrader struct { upgradeable bool } +// Action is the upgrade action state. +type Action interface { + // Version to upgrade to. + Version() string + // SourceURI for download. + SourceURI() string + // FleetAction is the action from fleet that started the action (optional). + FleetAction() *fleetapi.ActionUpgrade +} + type reexecManager interface { ReExec(argOverrides ...string) } @@ -60,13 +70,14 @@ type stateReporter interface { // NewUpgrader creates an upgrader which is capable of performing upgrade operation func NewUpgrader(agentInfo *info.AgentInfo, settings *artifact.Config, log *logger.Logger, closers []context.CancelFunc, reexec reexecManager, a acker, r stateReporter) *Upgrader { return &Upgrader{ + agentInfo: agentInfo, settings: settings, log: log, closers: closers, reexec: reexec, acker: a, reporter: r, - upgradeable: getUpgradable(), + upgradeable: getUpgradeable(), } } @@ -76,11 +87,13 @@ func (u *Upgrader) Upgradeable() bool { } // Upgrade upgrades running agent -func (u *Upgrader) Upgrade(ctx context.Context, a *fleetapi.ActionUpgrade) (err error) { +func (u *Upgrader) Upgrade(ctx context.Context, a Action, reexecNow bool) (err error) { // report failed defer func() { if err != nil { - u.reportFailure(ctx, a, err) + if action := a.FleetAction(); action != nil { + u.reportFailure(ctx, action, err) + } } }() @@ -90,15 +103,15 @@ func (u *Upgrader) Upgrade(ctx context.Context, a *fleetapi.ActionUpgrade) (err "running under control of the systems supervisor") } - u.reportUpdating(a.Version) + u.reportUpdating(a.Version()) - sourceURI, err := u.sourceURI(a.Version, a.SourceURI) - archivePath, err := u.downloadArtifact(ctx, a.Version, sourceURI) + sourceURI, err := u.sourceURI(a.Version(), a.SourceURI()) + archivePath, err := u.downloadArtifact(ctx, a.Version(), sourceURI) if err != nil { return err } - newHash, err := u.unpack(ctx, a.Version, archivePath) + newHash, err := u.unpack(ctx, a.Version(), archivePath) if err != nil { return err } @@ -109,7 +122,9 @@ func (u *Upgrader) Upgrade(ctx context.Context, a *fleetapi.ActionUpgrade) (err if strings.HasPrefix(release.Commit(), newHash) { // not an error - u.ackAction(ctx, a) + if action := a.FleetAction(); action != nil { + u.ackAction(ctx, action) + } u.log.Warn("upgrading to same version") return nil } @@ -128,7 +143,9 @@ func (u *Upgrader) Upgrade(ctx context.Context, a *fleetapi.ActionUpgrade) (err return err } - u.reexec.ReExec() + if reexecNow { + u.reexec.ReExec() + } return nil } @@ -224,7 +241,7 @@ func rollbackInstall(hash string) { os.RemoveAll(filepath.Join(paths.Data(), fmt.Sprintf("%s-%s", agentName, hash))) } -func getUpgradable() bool { +func getUpgradeable() bool { // only upgradeable if running from Agent installer and running under the // control of the system supervisor (or built specifically with upgrading enabled) return release.Upgradeable() || (install.RunningInstalled() && install.RunningUnderSupervisor()) diff --git a/x-pack/elastic-agent/pkg/agent/cmd/common.go b/x-pack/elastic-agent/pkg/agent/cmd/common.go index 8ca5700f3c6..39093be71b6 100644 --- a/x-pack/elastic-agent/pkg/agent/cmd/common.go +++ b/x-pack/elastic-agent/pkg/agent/cmd/common.go @@ -68,6 +68,7 @@ func NewCommandWithArgs(args []string, streams *cli.IOStreams) *cobra.Command { cmd.AddCommand(run) cmd.AddCommand(newInstallCommandWithArgs(flags, args, streams)) cmd.AddCommand(newUninstallCommandWithArgs(flags, args, streams)) + cmd.AddCommand(newUpgradeCommandWithArgs(flags, args, streams)) cmd.AddCommand(newEnrollCommandWithArgs(flags, args, streams)) cmd.AddCommand(newInspectCommandWithArgs(flags, args, streams)) diff --git a/x-pack/elastic-agent/pkg/agent/cmd/run.go b/x-pack/elastic-agent/pkg/agent/cmd/run.go index 77beeb6fe1a..84dd8bd8a9a 100644 --- a/x-pack/elastic-agent/pkg/agent/cmd/run.go +++ b/x-pack/elastic-agent/pkg/agent/cmd/run.go @@ -95,13 +95,13 @@ func run(flags *globalFlags, streams *cli.IOStreams) error { // Windows: Mark se rex := reexec.NewManager(rexLogger, execPath) // start the control listener - control := server.New(logger.Named("control"), rex) + control := server.New(logger.Named("control"), rex, nil) if err := control.Start(); err != nil { return err } defer control.Stop() - app, err := application.New(logger, pathConfigFile, rex) + app, err := application.New(logger, pathConfigFile, rex, control) if err != nil { return err } diff --git a/x-pack/elastic-agent/pkg/agent/cmd/upgrade.go b/x-pack/elastic-agent/pkg/agent/cmd/upgrade.go new file mode 100644 index 00000000000..81a5c82b4ab --- /dev/null +++ b/x-pack/elastic-agent/pkg/agent/cmd/upgrade.go @@ -0,0 +1,56 @@ +// 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 ( + "context" + "fmt" + "os" + + "github.com/spf13/cobra" + + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/control" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/control/client" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/cli" +) + +func newUpgradeCommandWithArgs(flags *globalFlags, _ []string, streams *cli.IOStreams) *cobra.Command { + cmd := &cobra.Command{ + Use: "upgrade ", + Short: "Upgrade the currently running Elastic Agent to the specified version", + Args: cobra.ExactArgs(1), + Run: func(c *cobra.Command, args []string) { + if err := upgradeCmd(streams, c, flags, args); err != nil { + fmt.Fprintf(streams.Err, "%v\n", err) + os.Exit(1) + } + }, + } + + cmd.Flags().StringP("source-uri", "s", "", "Source URI to download the new version from") + + return cmd +} + +func upgradeCmd(streams *cli.IOStreams, cmd *cobra.Command, flags *globalFlags, args []string) error { + fmt.Fprintln(streams.Out, "The upgrade process of Elastic Agent is currently EXPERIMENTAL and should not be used in production") + + version := args[0] + sourceURI, _ := cmd.Flags().GetString("source-uri") + + c := client.New() + err := c.Connect(context.Background()) + if err != nil { + return errors.New(err, "Failed communicating to running daemon", errors.TypeNetwork, errors.M("socket", control.Address())) + } + defer c.Disconnect() + version, err = c.Upgrade(context.Background(), version, sourceURI) + if err != nil { + return errors.New(err, "Failed trigger upgrade of daemon") + } + fmt.Fprintf(streams.Out, "Upgrade triggered to version %s, Elastic Agent is currently restarting\n", version) + return nil +} diff --git a/x-pack/elastic-agent/pkg/agent/control/control_test.go b/x-pack/elastic-agent/pkg/agent/control/control_test.go index 9454179ae60..5c56aed4691 100644 --- a/x-pack/elastic-agent/pkg/agent/control/control_test.go +++ b/x-pack/elastic-agent/pkg/agent/control/control_test.go @@ -20,7 +20,7 @@ import ( ) func TestServerClient_Version(t *testing.T) { - srv := server.New(newErrorLogger(t), nil) + srv := server.New(newErrorLogger(t), nil, nil) err := srv.Start() require.NoError(t, err) defer srv.Stop() diff --git a/x-pack/elastic-agent/pkg/agent/control/server/server.go b/x-pack/elastic-agent/pkg/agent/control/server/server.go index faa7982c814..0ce970c9256 100644 --- a/x-pack/elastic-agent/pkg/agent/control/server/server.go +++ b/x-pack/elastic-agent/pkg/agent/control/server/server.go @@ -7,14 +7,17 @@ package server import ( "context" "net" - - "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/reexec" + "sync" + "time" "google.golang.org/grpc" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/reexec" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/upgrade" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/control" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/control/proto" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/fleetapi" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/release" ) @@ -22,18 +25,28 @@ import ( type Server struct { logger *logger.Logger rex reexec.ExecManager + up *upgrade.Upgrader listener net.Listener server *grpc.Server + lock sync.RWMutex } // New creates a new control protocol server. -func New(log *logger.Logger, rex reexec.ExecManager) *Server { +func New(log *logger.Logger, rex reexec.ExecManager, up *upgrade.Upgrader) *Server { return &Server{ logger: log, rex: rex, + up: up, } } +// SetUpgrader changes the upgrader. +func (s *Server) SetUpgrader(up *upgrade.Upgrader) { + s.lock.Lock() + defer s.lock.Unlock() + s.up = up +} + // Start starts the GRPC endpoint and accepts new connections. func (s *Server) Start() error { if s.server != nil { @@ -100,10 +113,48 @@ func (s *Server) Restart(_ context.Context, _ *proto.Empty) (*proto.RestartRespo // Upgrade performs the upgrade operation. func (s *Server) Upgrade(ctx context.Context, request *proto.UpgradeRequest) (*proto.UpgradeResponse, error) { - // not implemented + s.lock.RLock() + u := s.up + s.lock.RUnlock() + if u == nil { + // not running with upgrader (must be controlled by Fleet) + return &proto.UpgradeResponse{ + Status: proto.ActionStatus_FAILURE, + Error: "cannot be upgraded; perform upgrading using Fleet", + }, nil + } + err := u.Upgrade(ctx, &upgradeRequest{request}, false) + if err != nil { + return &proto.UpgradeResponse{ + Status: proto.ActionStatus_FAILURE, + Error: err.Error(), + }, nil + } + // perform the re-exec after a 1 second delay + // this ensures that the upgrade response over GRPC is returned + go func() { + <-time.After(time.Second) + s.rex.ReExec() + }() return &proto.UpgradeResponse{ - Status: proto.ActionStatus_FAILURE, - Version: "", - Error: "not implemented", + Status: proto.ActionStatus_SUCCESS, + Version: request.Version, }, nil } + +type upgradeRequest struct { + *proto.UpgradeRequest +} + +func (r *upgradeRequest) Version() string { + return r.GetVersion() +} + +func (r *upgradeRequest) SourceURI() string { + return r.GetSourceURI() +} + +func (r *upgradeRequest) FleetAction() *fleetapi.ActionUpgrade { + // upgrade request not from Fleet + return nil +} diff --git a/x-pack/elastic-agent/pkg/basecmd/version/cmd_test.go b/x-pack/elastic-agent/pkg/basecmd/version/cmd_test.go index 119809338d6..6c656839820 100644 --- a/x-pack/elastic-agent/pkg/basecmd/version/cmd_test.go +++ b/x-pack/elastic-agent/pkg/basecmd/version/cmd_test.go @@ -52,7 +52,7 @@ func TestCmdBinaryOnlyYAML(t *testing.T) { } func TestCmdDaemon(t *testing.T) { - srv := server.New(newErrorLogger(t), nil) + srv := server.New(newErrorLogger(t), nil, nil) require.NoError(t, srv.Start()) defer srv.Stop() @@ -67,7 +67,7 @@ func TestCmdDaemon(t *testing.T) { } func TestCmdDaemonYAML(t *testing.T) { - srv := server.New(newErrorLogger(t), nil) + srv := server.New(newErrorLogger(t), nil, nil) require.NoError(t, srv.Start()) defer srv.Stop() From 889854e5f0ca01519487948a466177b897d46a82 Mon Sep 17 00:00:00 2001 From: Marc Guasch Date: Tue, 6 Oct 2020 09:38:30 +0200 Subject: [PATCH 26/93] Add missing changelog entry for cisco umbrella (#21550) --- CHANGELOG.next.asciidoc | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 279eda229a7..779272dc219 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -605,6 +605,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Add related.hosts ecs field to all modules {pull}21160[21160] - Keep cursor state between httpjson input restarts {pull}20751[20751] - Convert aws s3 to v2 input {pull}20005[20005] +- New Cisco Umbrella dataset {pull}21504[21504] *Heartbeat* From 76905a2e2f3bbd966fa5cffa1fe7ce7da1dd7b44 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Tue, 6 Oct 2020 10:41:47 +0200 Subject: [PATCH 27/93] Add a persistent cache for cloudfoundry metadata based on badger (#20775) Cache on disk is used by add_cloudfoundry_metadata. Cache is written into the beats data directory. Objects in cache are serialized using CBOR encoding. Badger DB is added as dependency. --- CHANGELOG.next.asciidoc | 1 + NOTICE.txt | 6521 +++++++++++------ go.mod | 6 +- go.sum | 66 +- libbeat/tests/system/test_cmd_completion.py | 2 +- x-pack/libbeat/common/cloudfoundry/cache.go | 109 +- .../cloudfoundry/cache_integration_test.go | 11 +- .../libbeat/common/cloudfoundry/cache_test.go | 27 +- x-pack/libbeat/common/cloudfoundry/hub.go | 75 +- .../libbeat/common/cloudfoundry/main_test.go | 29 + x-pack/libbeat/persistentcache/encoding.go | 55 + .../persistentcache/persistentcache.go | 112 + .../persistentcache/persistentcache_test.go | 436 ++ x-pack/libbeat/persistentcache/store.go | 141 + x-pack/libbeat/persistentcache/store_test.go | 43 + .../add_cloudfoundry_metadata.go | 30 +- .../add_cloudfoundry_metadata_test.go | 46 +- 17 files changed, 5487 insertions(+), 2223 deletions(-) create mode 100644 x-pack/libbeat/common/cloudfoundry/main_test.go create mode 100644 x-pack/libbeat/persistentcache/encoding.go create mode 100644 x-pack/libbeat/persistentcache/persistentcache.go create mode 100644 x-pack/libbeat/persistentcache/persistentcache_test.go create mode 100644 x-pack/libbeat/persistentcache/store.go create mode 100644 x-pack/libbeat/persistentcache/store_test.go diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 779272dc219..5676634d637 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -448,6 +448,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Added experimental dataset `juniper/netscreen`. {pull}20820[20820] - Added experimental dataset `sophos/utm`. {pull}20820[20820] - Add Cloud Foundry tags in related events. {pull}21177[21177] +- Cloud Foundry metadata is cached to disk. {pull}20775[20775] - Add option to select the type of index template to load: legacy, component, index. {pull}21212[21212] *Auditbeat* diff --git a/NOTICE.txt b/NOTICE.txt index 527c1304379..0017abeba1a 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -4472,6 +4472,192 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- +Dependency : github.com/dgraph-io/badger/v2 +Version: v2.2007.2 +Licence type (autodetected): Apache-2.0 +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/dgraph-io/badger/v2@v2.2007.2/LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + -------------------------------------------------------------------------------- Dependency : github.com/digitalocean/go-libvirt Version: v0.0.0-20180301200012-6075ea3c39a1 @@ -5542,11 +5728,11 @@ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -------------------------------------------------------------------------------- Dependency : github.com/dustin/go-humanize -Version: v0.0.0-20171111073723-bb3d318650d4 +Version: v1.0.0 Licence type (autodetected): MIT -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/dustin/go-humanize@v0.0.0-20171111073723-bb3d318650d4/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/dustin/go-humanize@v1.0.0/LICENSE: Copyright (c) 2005-2008 Dustin Sallings @@ -13066,11 +13252,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- Dependency : github.com/spf13/cobra -Version: v0.0.3 +Version: v0.0.5 Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/spf13/cobra@v0.0.3/LICENSE.txt: +Contents of probable licence file $GOMODCACHE/github.com/spf13/cobra@v0.0.5/LICENSE.txt: Apache License Version 2.0, January 2004 @@ -13393,224 +13579,256 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- -Dependency : github.com/urso/sderr -Version: v0.0.0-20200210124243-c2a16f3d43ec -Licence type (autodetected): Apache-2.0 +Dependency : github.com/ugorji/go/codec +Version: v1.1.8 +Licence type (autodetected): MIT -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/urso/sderr@v0.0.0-20200210124243-c2a16f3d43ec/LICENSE: - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. +Contents of probable licence file $GOMODCACHE/github.com/ugorji/go/codec@v1.1.8/LICENSE: - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. +The MIT License (MIT) - Copyright [yyyy] [name of copyright owner] +Copyright (c) 2012-2015 Ugorji Nwoke. +All rights reserved. - Licensed 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 +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - http://www.apache.org/licenses/LICENSE-2.0 +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - 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. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. -------------------------------------------------------------------------------- -Dependency : github.com/vmware/govmomi -Version: v0.0.0-20170802214208-2cad15190b41 +Dependency : github.com/urso/sderr +Version: v0.0.0-20200210124243-c2a16f3d43ec Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/vmware/govmomi@v0.0.0-20170802214208-2cad15190b41/LICENSE.txt: - +Contents of probable licence file $GOMODCACHE/github.com/urso/sderr@v0.0.0-20200210124243-c2a16f3d43ec/LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. + + +-------------------------------------------------------------------------------- +Dependency : github.com/vmware/govmomi +Version: v0.0.0-20170802214208-2cad15190b41 +Licence type (autodetected): Apache-2.0 +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/vmware/govmomi@v0.0.0-20170802214208-2cad15190b41/LICENSE.txt: + Apache License Version 2.0, January 2004 @@ -19482,6 +19700,43 @@ Contents of probable licence file $GOMODCACHE/github.com/!burnt!sushi/xgb@v0.0.0 // such litigation is filed. +-------------------------------------------------------------------------------- +Dependency : github.com/DataDog/zstd +Version: v1.4.1 +Licence type (autodetected): BSD-3-Clause +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/!data!dog/zstd@v1.4.1/LICENSE: + +Simplified BSD License + +Copyright (c) 2016, Datadog +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + -------------------------------------------------------------------------------- Dependency : github.com/Masterminds/semver Version: v1.4.2 @@ -19565,6 +19820,203 @@ See the License for the specific language governing permissions and limitations under the License. +-------------------------------------------------------------------------------- +Dependency : github.com/OneOfOne/xxhash +Version: v1.2.2 +Licence type (autodetected): Apache-2.0 +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/!one!of!one/xxhash@v1.2.2/LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + -------------------------------------------------------------------------------- Dependency : github.com/PuerkitoBio/purell Version: v1.0.0 @@ -19802,6 +20254,377 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-------------------------------------------------------------------------------- +Dependency : github.com/armon/consul-api +Version: v0.0.0-20180202201655-eb2c6b5be1b6 +Licence type (autodetected): MPL-2.0 +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/armon/consul-api@v0.0.0-20180202201655-eb2c6b5be1b6/LICENSE: + +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. "Contributor" + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. "Contributor Version" + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the terms of + a Secondary License. + +1.6. "Executable Form" + + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + + means a work that combines Covered Software with other material, in a + separate file or files, that is not Covered Software. + +1.8. "License" + + means this document. + +1.9. "Licensable" + + means having the right to grant, to the maximum extent possible, whether + at the time of the initial grant or subsequently, any and all of the + rights conveyed by this License. + +1.10. "Modifications" + + means any of the following: + + a. any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. "Patent Claims" of a Contributor + + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the License, + by the making, using, selling, offering for sale, having made, import, + or transfer of either its Contributions or its Contributor Version. + +1.12. "Secondary License" + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. "Source Code Form" + + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, "control" means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution + become effective for each Contribution on the date the Contributor first + distributes such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under + this License. No additional rights or licenses will be implied from the + distribution or licensing of Covered Software under this License. + Notwithstanding Section 2.1(b) above, no patent license is granted by a + Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of + its Contributions. + + This License does not grant any rights in the trademarks, service marks, + or logos of any Contributor (except as may be necessary to comply with + the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this + License (see Section 10.2) or under the terms of a Secondary License (if + permitted under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its + Contributions are its original creation(s) or it has sufficient rights to + grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under + applicable copyright doctrines of fair use, fair dealing, or other + equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under + the terms of this License. You must inform recipients that the Source + Code Form of the Covered Software is governed by the terms of this + License, and how they can obtain a copy of this License. You may not + attempt to alter or restrict the recipients' rights in the Source Code + Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter the + recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for + the Covered Software. If the Larger Work is a combination of Covered + Software with a work governed by one or more Secondary Licenses, and the + Covered Software is not Incompatible With Secondary Licenses, this + License permits You to additionally distribute such Covered Software + under the terms of such Secondary License(s), so that the recipient of + the Larger Work may, at their option, further distribute the Covered + Software under the terms of either this License or such Secondary + License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices + (including copyright notices, patent notices, disclaimers of warranty, or + limitations of liability) contained within the Source Code Form of the + Covered Software, except that You may alter any license notices to the + extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on + behalf of any Contributor. You must make it absolutely clear that any + such warranty, support, indemnity, or liability obligation is offered by + You alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, + judicial order, or regulation then You must: (a) comply with the terms of + this License to the maximum extent possible; and (b) describe the + limitations and the code they affect. Such description must be placed in a + text file included with all distributions of the Covered Software under + this License. Except to the extent prohibited by statute or regulation, + such description must be sufficiently detailed for a recipient of ordinary + skill to be able to understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing + basis, if such Contributor fails to notify You of the non-compliance by + some reasonable means prior to 60 days after You have come back into + compliance. Moreover, Your grants from a particular Contributor are + reinstated on an ongoing basis if such Contributor notifies You of the + non-compliance by some reasonable means, this is the first time You have + received notice of non-compliance with this License from such + Contributor, and You become compliant prior to 30 days after Your receipt + of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, + counter-claims, and cross-claims) alleging that a Contributor Version + directly or indirectly infringes any patent, then the rights granted to + You by any and all Contributors for the Covered Software under Section + 2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an "as is" basis, + without warranty of any kind, either expressed, implied, or statutory, + including, without limitation, warranties that the Covered Software is free + of defects, merchantable, fit for a particular purpose or non-infringing. + The entire risk as to the quality and performance of the Covered Software + is with You. Should any Covered Software prove defective in any respect, + You (not any Contributor) assume the cost of any necessary servicing, + repair, or correction. This disclaimer of warranty constitutes an essential + part of this License. No use of any Covered Software is authorized under + this License except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from + such party's negligence to the extent applicable law prohibits such + limitation. Some jurisdictions do not allow the exclusion or limitation of + incidental or consequential damages, so this exclusion and limitation may + not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts + of a jurisdiction where the defendant maintains its principal place of + business and such litigation shall be governed by laws of that + jurisdiction, without reference to its conflict-of-law provisions. Nothing + in this Section shall prevent a party's ability to bring cross-claims or + counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. Any law or regulation which provides that + the language of a contract shall be construed against the drafter shall not + be used to construe this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version + of the License under which You originally received the Covered Software, + or under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a + modified version of this License if you rename the license and remove + any references to the name of the license steward (except to note that + such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary + Licenses If You choose to distribute Source Code Form that is + Incompatible With Secondary Licenses under the terms of this version of + the License, the notice described in Exhibit B of this License must be + attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, +then You may include the notice in a location (such as a LICENSE file in a +relevant directory) where a recipient would be likely to look for such a +notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice + + This Source Code Form is "Incompatible + With Secondary Licenses", as defined by + the Mozilla Public License, v. 2.0. + -------------------------------------------------------------------------------- Dependency : github.com/armon/go-radix Version: v1.0.0 @@ -20546,6 +21369,38 @@ Contents of probable licence file $GOMODCACHE/github.com/census-instrumentation/ limitations under the License. +-------------------------------------------------------------------------------- +Dependency : github.com/cespare/xxhash +Version: v1.1.0 +Licence type (autodetected): MIT +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/cespare/xxhash@v1.1.0/LICENSE.txt: + +Copyright (c) 2016 Caleb Spare + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------------- Dependency : github.com/chzyer/logex Version: v1.1.10 @@ -22348,378 +23203,13 @@ Contents of probable licence file $GOMODCACHE/github.com/containerd/typeurl@v0.0 -------------------------------------------------------------------------------- -Dependency : github.com/coreos/go-systemd -Version: v0.0.0-20190321100706-95778dfbb74e +Dependency : github.com/coreos/etcd +Version: v3.3.10+incompatible Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/coreos/go-systemd@v0.0.0-20190321100706-95778dfbb74e/LICENSE: - -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed 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. - - --------------------------------------------------------------------------------- -Dependency : github.com/cucumber/godog -Version: v0.8.1 -Licence type (autodetected): MIT --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/cucumber/godog@v0.8.1/LICENSE: - -The MIT License (MIT) - -Copyright (c) SmartBear - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - --------------------------------------------------------------------------------- -Dependency : github.com/cyphar/filepath-securejoin -Version: v0.2.2 -Licence type (autodetected): BSD-3-Clause --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/cyphar/filepath-securejoin@v0.2.2/LICENSE: - -Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved. -Copyright (C) 2017 SUSE LLC. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - +Contents of probable licence file $GOMODCACHE/github.com/coreos/etcd@v3.3.10+incompatible/LICENSE: --------------------------------------------------------------------------------- -Dependency : github.com/davecgh/go-spew -Version: v1.1.1 -Licence type (autodetected): ISC --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/davecgh/go-spew@v1.1.1/LICENSE: - -ISC License - -Copyright (c) 2012-2016 Dave Collins - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - --------------------------------------------------------------------------------- -Dependency : github.com/davecgh/go-xdr -Version: v0.0.0-20161123171359-e6a2ba005892 -Licence type (autodetected): ISC --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/davecgh/go-xdr@v0.0.0-20161123171359-e6a2ba005892/LICENSE: - -Copyright (c) 2012-2014 Dave Collins - -Permission to use, copy, modify, and distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - --------------------------------------------------------------------------------- -Dependency : github.com/devigned/tab -Version: v0.1.2-0.20190607222403-0c15cf42f9a2 -Licence type (autodetected): MIT --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/devigned/tab@v0.1.2-0.20190607222403-0c15cf42f9a2/LICENSE: - -MIT License - -Copyright (c) 2019 David Justice - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - --------------------------------------------------------------------------------- -Dependency : github.com/dgrijalva/jwt-go -Version: v3.2.1-0.20190620180102-5e25c22bd5d6+incompatible -Licence type (autodetected): MIT --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/dgrijalva/jwt-go@v3.2.1-0.20190620180102-5e25c22bd5d6+incompatible/LICENSE: - -Copyright (c) 2012 Dave Grijalva - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - - --------------------------------------------------------------------------------- -Dependency : github.com/dimchansky/utfbom -Version: v1.1.0 -Licence type (autodetected): Apache-2.0 --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/dimchansky/utfbom@v1.1.0/LICENSE: Apache License Version 2.0, January 2004 @@ -22901,7 +23391,7 @@ Contents of probable licence file $GOMODCACHE/github.com/dimchansky/utfbom@v1.1. APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" + boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -22909,7 +23399,7 @@ Contents of probable licence file $GOMODCACHE/github.com/dimchansky/utfbom@v1.1. same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright {yyyy} {name of copyright owner} + Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -22925,45 +23415,15 @@ Contents of probable licence file $GOMODCACHE/github.com/dimchansky/utfbom@v1.1. -------------------------------------------------------------------------------- -Dependency : github.com/dlclark/regexp2 -Version: v1.1.7-0.20171009020623-7632a260cbaf -Licence type (autodetected): MIT --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/dlclark/regexp2@v1.1.7-0.20171009020623-7632a260cbaf/LICENSE: - -The MIT License (MIT) - -Copyright (c) Doug Clark - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - --------------------------------------------------------------------------------- -Dependency : github.com/docker/distribution -Version: v2.7.1+incompatible +Dependency : github.com/coreos/go-etcd +Version: v2.0.0+incompatible Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/docker/distribution@v2.7.1+incompatible/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/coreos/go-etcd@v2.0.0+incompatible/LICENSE: -Apache License + + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -23143,7 +23603,7 @@ Apache License APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" + boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -23151,7 +23611,7 @@ Apache License same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright {yyyy} {name of copyright owner} + Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -23166,19 +23626,18 @@ Apache License limitations under the License. - -------------------------------------------------------------------------------- -Dependency : github.com/docker/go-metrics -Version: v0.0.1 +Dependency : github.com/coreos/go-semver +Version: v0.2.0 Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/docker/go-metrics@v0.0.1/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/coreos/go-semver@v0.2.0/LICENSE: Apache License Version 2.0, January 2004 - https://www.apache.org/licenses/ + http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -23353,13 +23812,24 @@ Contents of probable licence file $GOMODCACHE/github.com/docker/go-metrics@v0.0. END OF TERMS AND CONDITIONS - Copyright 2013-2016 Docker, Inc. + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] Licensed 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 - https://www.apache.org/licenses/LICENSE-2.0 + 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, @@ -23369,198 +23839,198 @@ Contents of probable licence file $GOMODCACHE/github.com/docker/go-metrics@v0.0. -------------------------------------------------------------------------------- -Dependency : github.com/docker/spdystream -Version: v0.0.0-20160310174837-449fdfce4d96 +Dependency : github.com/coreos/go-systemd +Version: v0.0.0-20190321100706-95778dfbb74e Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/docker/spdystream@v0.0.0-20160310174837-449fdfce4d96/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/coreos/go-systemd@v0.0.0-20190321100706-95778dfbb74e/LICENSE: +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +1. Definitions. - 1. Definitions. +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. +2. Grant of Copyright License. - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. +3. Grant of Patent License. - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and +4. Redistribution. - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. +5. Submission of Contributions. - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. +6. Trademarks. - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. +7. Disclaimer of Warranty. - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. +8. Limitation of Liability. - END OF TERMS AND CONDITIONS +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. - Copyright 2014-2015 Docker, Inc. +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] Licensed 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 + 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, @@ -23570,16 +24040,16 @@ Contents of probable licence file $GOMODCACHE/github.com/docker/spdystream@v0.0. -------------------------------------------------------------------------------- -Dependency : github.com/eapache/go-resiliency -Version: v1.2.0 +Dependency : github.com/cpuguy83/go-md2man +Version: v1.0.10 Licence type (autodetected): MIT -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/eapache/go-resiliency@v1.2.0/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/cpuguy83/go-md2man@v1.0.10/LICENSE.md: The MIT License (MIT) -Copyright (c) 2014 Evan Huus +Copyright (c) 2014 Brian Goff Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -23600,18 +24070,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -------------------------------------------------------------------------------- -Dependency : github.com/eapache/go-xerial-snappy -Version: v0.0.0-20180814174437-776d5712da21 +Dependency : github.com/cucumber/godog +Version: v0.8.1 Licence type (autodetected): MIT -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/eapache/go-xerial-snappy@v0.0.0-20180814174437-776d5712da21/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/cucumber/godog@v0.8.1/LICENSE: The MIT License (MIT) -Copyright (c) 2016 Evan Huus +Copyright (c) SmartBear Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -23633,16 +24102,101 @@ SOFTWARE. -------------------------------------------------------------------------------- -Dependency : github.com/eapache/queue -Version: v1.1.0 +Dependency : github.com/cyphar/filepath-securejoin +Version: v0.2.2 +Licence type (autodetected): BSD-3-Clause +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/cyphar/filepath-securejoin@v0.2.2/LICENSE: + +Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved. +Copyright (C) 2017 SUSE LLC. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +-------------------------------------------------------------------------------- +Dependency : github.com/davecgh/go-spew +Version: v1.1.1 +Licence type (autodetected): ISC +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/davecgh/go-spew@v1.1.1/LICENSE: + +ISC License + +Copyright (c) 2012-2016 Dave Collins + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +-------------------------------------------------------------------------------- +Dependency : github.com/davecgh/go-xdr +Version: v0.0.0-20161123171359-e6a2ba005892 +Licence type (autodetected): ISC +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/davecgh/go-xdr@v0.0.0-20161123171359-e6a2ba005892/LICENSE: + +Copyright (c) 2012-2014 Dave Collins + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +-------------------------------------------------------------------------------- +Dependency : github.com/devigned/tab +Version: v0.1.2-0.20190607222403-0c15cf42f9a2 Licence type (autodetected): MIT -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/eapache/queue@v1.1.0/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/devigned/tab@v0.1.2-0.20190607222403-0c15cf42f9a2/LICENSE: -The MIT License (MIT) +MIT License -Copyright (c) 2014 Evan Huus +Copyright (c) 2019 David Justice Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -23662,14 +24216,14 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + -------------------------------------------------------------------------------- -Dependency : github.com/elastic/go-windows -Version: v1.0.1 +Dependency : github.com/dgraph-io/ristretto +Version: v0.0.3-0.20200630154024-f66de99634de Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/elastic/go-windows@v1.0.1/LICENSE.txt: - +Contents of probable licence file $GOMODCACHE/github.com/dgraph-io/ristretto@v0.0.3-0.20200630154024-f66de99634de/LICENSE: Apache License Version 2.0, January 2004 @@ -23848,107 +24402,65 @@ Contents of probable licence file $GOMODCACHE/github.com/elastic/go-windows@v1.0 END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. +-------------------------------------------------------------------------------- +Dependency : github.com/dgrijalva/jwt-go +Version: v3.2.1-0.20190620180102-5e25c22bd5d6+incompatible +Licence type (autodetected): MIT +-------------------------------------------------------------------------------- - Copyright [yyyy] [name of copyright owner] +Contents of probable licence file $GOMODCACHE/github.com/dgrijalva/jwt-go@v3.2.1-0.20190620180102-5e25c22bd5d6+incompatible/LICENSE: - Licensed 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 +Copyright (c) 2012 Dave Grijalva - 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. - - --------------------------------------------------------------------------------- -Dependency : github.com/elazarl/goproxy -Version: v0.0.0-20180725130230-947c36da3153 -Licence type (autodetected): BSD-3-Clause --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/elazarl/goproxy@v0.0.0-20180725130230-947c36da3153/LICENSE: - -Copyright (c) 2012 Elazar Leibovich. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Elazar Leibovich. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- -Dependency : github.com/emicklei/go-restful -Version: v0.0.0-20170410110728-ff4f55a20633 +Dependency : github.com/dgryski/go-farm +Version: v0.0.0-20190423205320-6a90982ecee2 Licence type (autodetected): MIT -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/emicklei/go-restful@v0.0.0-20170410110728-ff4f55a20633/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/dgryski/go-farm@v0.0.0-20190423205320-6a90982ecee2/LICENSE: -Copyright (c) 2012,2013 Ernest Micklei +As this is a highly derivative work, I have placed it under the same license as the original implementation: -MIT License +Copyright (c) 2014-2017 Damian Gryski +Copyright (c) 2016-2017 Nicola Asuni - Tecnick.com -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- -Dependency : github.com/envoyproxy/go-control-plane -Version: v0.9.4 +Dependency : github.com/dimchansky/utfbom +Version: v1.1.0 Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/envoyproxy/go-control-plane@v0.9.4/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/dimchansky/utfbom@v1.1.0/LICENSE: Apache License Version 2.0, January 2004 @@ -24154,15 +24666,45 @@ Contents of probable licence file $GOMODCACHE/github.com/envoyproxy/go-control-p -------------------------------------------------------------------------------- -Dependency : github.com/envoyproxy/protoc-gen-validate -Version: v0.1.0 -Licence type (autodetected): Apache-2.0 +Dependency : github.com/dlclark/regexp2 +Version: v1.1.7-0.20171009020623-7632a260cbaf +Licence type (autodetected): MIT -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/envoyproxy/protoc-gen-validate@v0.1.0/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/dlclark/regexp2@v1.1.7-0.20171009020623-7632a260cbaf/LICENSE: + +The MIT License (MIT) +Copyright (c) Doug Clark - Apache License +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +-------------------------------------------------------------------------------- +Dependency : github.com/docker/distribution +Version: v2.7.1+incompatible +Licence type (autodetected): Apache-2.0 +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/docker/distribution@v2.7.1+incompatible/LICENSE: + +Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -24342,7 +24884,7 @@ Contents of probable licence file $GOMODCACHE/github.com/envoyproxy/protoc-gen-v APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" + boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -24350,7 +24892,7 @@ Contents of probable licence file $GOMODCACHE/github.com/envoyproxy/protoc-gen-v same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -24365,281 +24907,19 @@ Contents of probable licence file $GOMODCACHE/github.com/envoyproxy/protoc-gen-v limitations under the License. --------------------------------------------------------------------------------- -Dependency : github.com/evanphx/json-patch -Version: v4.2.0+incompatible -Licence type (autodetected): BSD-3-Clause --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/evanphx/json-patch@v4.2.0+incompatible/LICENSE: - -Copyright (c) 2014, Evan Phoenix -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. -* Redistributions in binary form must reproduce the above copyright notice - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. -* Neither the name of the Evan Phoenix nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - --------------------------------------------------------------------------------- -Dependency : github.com/fortytw2/leaktest -Version: v1.3.0 -Licence type (autodetected): BSD-3-Clause --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/fortytw2/leaktest@v1.3.0/LICENSE: - -Copyright (c) 2012 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - --------------------------------------------------------------------------------- -Dependency : github.com/frankban/quicktest -Version: v1.7.2 -Licence type (autodetected): MIT --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/frankban/quicktest@v1.7.2/LICENSE: - -MIT License - -Copyright (c) 2017 Canonical Ltd. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - --------------------------------------------------------------------------------- -Dependency : github.com/ghodss/yaml -Version: v1.0.0 -Licence type (autodetected): MIT --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/ghodss/yaml@v1.0.0/LICENSE: - -The MIT License (MIT) - -Copyright (c) 2014 Sam Ghods - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - -Copyright (c) 2012 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - --------------------------------------------------------------------------------- -Dependency : github.com/go-gl/glfw/v3.3/glfw -Version: v0.0.0-20191125211704-12ad95a8df72 -Licence type (autodetected): BSD-3-Clause --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/go-gl/glfw/v3.3/glfw@v0.0.0-20191125211704-12ad95a8df72/LICENSE: - -Copyright (c) 2012 The glfw3-go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - --------------------------------------------------------------------------------- -Dependency : github.com/go-kit/kit -Version: v0.9.0 -Licence type (autodetected): MIT --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/go-kit/kit@v0.9.0/LICENSE: - -The MIT License (MIT) - -Copyright (c) 2015 Peter Bourgon - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - - --------------------------------------------------------------------------------- -Dependency : github.com/go-logfmt/logfmt -Version: v0.4.0 -Licence type (autodetected): MIT --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/go-logfmt/logfmt@v0.4.0/LICENSE: - -The MIT License (MIT) - -Copyright (c) 2015 go-logfmt - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - -------------------------------------------------------------------------------- -Dependency : github.com/go-logr/logr -Version: v0.1.0 +Dependency : github.com/docker/go-metrics +Version: v0.0.1 Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/go-logr/logr@v0.1.0/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/docker/go-metrics@v0.0.1/LICENSE: + Apache License Version 2.0, January 2004 - http://www.apache.org/licenses/ + https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -24814,24 +25094,13 @@ Contents of probable licence file $GOMODCACHE/github.com/go-logr/logr@v0.1.0/LIC END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} + Copyright 2013-2016 Docker, Inc. Licensed 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 + https://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, @@ -24841,73 +25110,12 @@ Contents of probable licence file $GOMODCACHE/github.com/go-logr/logr@v0.1.0/LIC -------------------------------------------------------------------------------- -Dependency : github.com/go-martini/martini -Version: v0.0.0-20170121215854-22fa46961aab -Licence type (autodetected): MIT --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/go-martini/martini@v0.0.0-20170121215854-22fa46961aab/LICENSE: - -The MIT License (MIT) - -Copyright (c) 2015 Jeremy Saenz - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - --------------------------------------------------------------------------------- -Dependency : github.com/go-ole/go-ole -Version: v1.2.5-0.20190920104607-14974a1cf647 -Licence type (autodetected): MIT --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/go-ole/go-ole@v1.2.5-0.20190920104607-14974a1cf647/LICENSE: - -The MIT License (MIT) - -Copyright © 2013-2017 Yasuhiro Matsumoto, - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the “Software”), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - --------------------------------------------------------------------------------- -Dependency : github.com/go-openapi/jsonpointer -Version: v0.0.0-20160704185906-46af16f9f7b1 +Dependency : github.com/docker/spdystream +Version: v0.0.0-20160310174837-449fdfce4d96 Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/go-openapi/jsonpointer@v0.0.0-20160704185906-46af16f9f7b1/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/docker/spdystream@v0.0.0-20160310174837-449fdfce4d96/LICENSE: Apache License @@ -25087,18 +25295,7 @@ Contents of probable licence file $GOMODCACHE/github.com/go-openapi/jsonpointer@ END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] + Copyright 2014-2015 Docker, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -25114,19 +25311,112 @@ Contents of probable licence file $GOMODCACHE/github.com/go-openapi/jsonpointer@ -------------------------------------------------------------------------------- -Dependency : github.com/go-openapi/jsonreference -Version: v0.0.0-20160704190145-13c6e3589ad9 -Licence type (autodetected): Apache-2.0 +Dependency : github.com/eapache/go-resiliency +Version: v1.2.0 +Licence type (autodetected): MIT -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/go-openapi/jsonreference@v0.0.0-20160704190145-13c6e3589ad9/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/eapache/go-resiliency@v1.2.0/LICENSE: +The MIT License (MIT) - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ +Copyright (c) 2014 Evan Huus - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +-------------------------------------------------------------------------------- +Dependency : github.com/eapache/go-xerial-snappy +Version: v0.0.0-20180814174437-776d5712da21 +Licence type (autodetected): MIT +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/eapache/go-xerial-snappy@v0.0.0-20180814174437-776d5712da21/LICENSE: + +The MIT License (MIT) + +Copyright (c) 2016 Evan Huus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +-------------------------------------------------------------------------------- +Dependency : github.com/eapache/queue +Version: v1.1.0 +Licence type (autodetected): MIT +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/eapache/queue@v1.1.0/LICENSE: + +The MIT License (MIT) + +Copyright (c) 2014 Evan Huus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +-------------------------------------------------------------------------------- +Dependency : github.com/elastic/go-windows +Version: v1.0.1 +Licence type (autodetected): Apache-2.0 +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/elastic/go-windows@v1.0.1/LICENSE.txt: + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. @@ -25326,13 +25616,80 @@ Contents of probable licence file $GOMODCACHE/github.com/go-openapi/jsonreferenc -------------------------------------------------------------------------------- -Dependency : github.com/go-openapi/spec -Version: v0.0.0-20160808142527-6aced65f8501 -Licence type (autodetected): Apache-2.0 +Dependency : github.com/elazarl/goproxy +Version: v0.0.0-20180725130230-947c36da3153 +Licence type (autodetected): BSD-3-Clause -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/go-openapi/spec@v0.0.0-20160808142527-6aced65f8501/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/elazarl/goproxy@v0.0.0-20180725130230-947c36da3153/LICENSE: +Copyright (c) 2012 Elazar Leibovich. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Elazar Leibovich. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +-------------------------------------------------------------------------------- +Dependency : github.com/emicklei/go-restful +Version: v0.0.0-20170410110728-ff4f55a20633 +Licence type (autodetected): MIT +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/emicklei/go-restful@v0.0.0-20170410110728-ff4f55a20633/LICENSE: + +Copyright (c) 2012,2013 Ernest Micklei + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +-------------------------------------------------------------------------------- +Dependency : github.com/envoyproxy/go-control-plane +Version: v0.9.4 +Licence type (autodetected): Apache-2.0 +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/envoyproxy/go-control-plane@v0.9.4/LICENSE: Apache License Version 2.0, January 2004 @@ -25514,7 +25871,7 @@ Contents of probable licence file $GOMODCACHE/github.com/go-openapi/spec@v0.0.0- APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" + boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -25522,7 +25879,7 @@ Contents of probable licence file $GOMODCACHE/github.com/go-openapi/spec@v0.0.0- same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -25538,12 +25895,12 @@ Contents of probable licence file $GOMODCACHE/github.com/go-openapi/spec@v0.0.0- -------------------------------------------------------------------------------- -Dependency : github.com/go-openapi/swag -Version: v0.0.0-20160704191624-1d0bd113de87 +Dependency : github.com/envoyproxy/protoc-gen-validate +Version: v0.1.0 Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/go-openapi/swag@v0.0.0-20160704191624-1d0bd113de87/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/envoyproxy/protoc-gen-validate@v0.1.0/LICENSE: Apache License @@ -25750,16 +26107,50 @@ Contents of probable licence file $GOMODCACHE/github.com/go-openapi/swag@v0.0.0- -------------------------------------------------------------------------------- -Dependency : github.com/go-sourcemap/sourcemap -Version: v2.1.2+incompatible -Licence type (autodetected): BSD-2-Clause +Dependency : github.com/evanphx/json-patch +Version: v4.2.0+incompatible +Licence type (autodetected): BSD-3-Clause -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/go-sourcemap/sourcemap@v2.1.2+incompatible/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/evanphx/json-patch@v4.2.0+incompatible/LICENSE: -Copyright (c) 2016 The github.com/go-sourcemap/sourcemap Contributors. +Copyright (c) 2014, Evan Phoenix All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* Neither the name of the Evan Phoenix nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +-------------------------------------------------------------------------------- +Dependency : github.com/fortytw2/leaktest +Version: v1.3.0 +Licence type (autodetected): BSD-3-Clause +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/fortytw2/leaktest@v1.3.0/LICENSE: + +Copyright (c) 2012 The Go Authors. All rights reserved. + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -25770,6 +26161,9 @@ notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -25785,16 +26179,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- -Dependency : github.com/go-stack/stack -Version: v1.8.0 +Dependency : github.com/frankban/quicktest +Version: v1.7.2 Licence type (autodetected): MIT -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/go-stack/stack@v1.8.0/LICENSE.md: +Contents of probable licence file $GOMODCACHE/github.com/frankban/quicktest@v1.7.2/LICENSE: -The MIT License (MIT) +MIT License -Copyright (c) 2014 Chris Hines +Copyright (c) 2017 Canonical Ltd. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -25816,16 +26210,16 @@ SOFTWARE. -------------------------------------------------------------------------------- -Dependency : github.com/gobuffalo/here -Version: v0.6.0 +Dependency : github.com/ghodss/yaml +Version: v1.0.0 Licence type (autodetected): MIT -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/gobuffalo/here@v0.6.0/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/ghodss/yaml@v1.0.0/LICENSE: The MIT License (MIT) -Copyright (c) 2019 Mark Bates +Copyright (c) 2014 Sam Ghods Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -25846,48 +26240,1395 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + -------------------------------------------------------------------------------- -Dependency : github.com/godbus/dbus/v5 -Version: v5.0.3 -Licence type (autodetected): BSD-2-Clause +Dependency : github.com/go-gl/glfw/v3.3/glfw +Version: v0.0.0-20191125211704-12ad95a8df72 +Licence type (autodetected): BSD-3-Clause -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/godbus/dbus/v5@v5.0.3/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/go-gl/glfw/v3.3/glfw@v0.0.0-20191125211704-12ad95a8df72/LICENSE: -Copyright (c) 2013, Georg Reinke (), Google -All rights reserved. +Copyright (c) 2012 The glfw3-go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright notice, -this list of conditions and the following disclaimer. +modification, are permitted provided that the following conditions are +met: -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- -Dependency : github.com/golang-sql/civil -Version: v0.0.0-20190719163853-cb61b32ac6fe -Licence type (autodetected): Apache-2.0 +Dependency : github.com/go-kit/kit +Version: v0.9.0 +Licence type (autodetected): MIT -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/golang-sql/civil@v0.0.0-20190719163853-cb61b32ac6fe/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/go-kit/kit@v0.9.0/LICENSE: + +The MIT License (MIT) + +Copyright (c) 2015 Peter Bourgon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +-------------------------------------------------------------------------------- +Dependency : github.com/go-logfmt/logfmt +Version: v0.4.0 +Licence type (autodetected): MIT +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/go-logfmt/logfmt@v0.4.0/LICENSE: + +The MIT License (MIT) + +Copyright (c) 2015 go-logfmt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +-------------------------------------------------------------------------------- +Dependency : github.com/go-logr/logr +Version: v0.1.0 +Licence type (autodetected): Apache-2.0 +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/go-logr/logr@v0.1.0/LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed 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. + + +-------------------------------------------------------------------------------- +Dependency : github.com/go-martini/martini +Version: v0.0.0-20170121215854-22fa46961aab +Licence type (autodetected): MIT +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/go-martini/martini@v0.0.0-20170121215854-22fa46961aab/LICENSE: + +The MIT License (MIT) + +Copyright (c) 2015 Jeremy Saenz + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +-------------------------------------------------------------------------------- +Dependency : github.com/go-ole/go-ole +Version: v1.2.5-0.20190920104607-14974a1cf647 +Licence type (autodetected): MIT +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/go-ole/go-ole@v1.2.5-0.20190920104607-14974a1cf647/LICENSE: + +The MIT License (MIT) + +Copyright © 2013-2017 Yasuhiro Matsumoto, + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the “Software”), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +-------------------------------------------------------------------------------- +Dependency : github.com/go-openapi/jsonpointer +Version: v0.0.0-20160704185906-46af16f9f7b1 +Licence type (autodetected): Apache-2.0 +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/go-openapi/jsonpointer@v0.0.0-20160704185906-46af16f9f7b1/LICENSE: + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. + + +-------------------------------------------------------------------------------- +Dependency : github.com/go-openapi/jsonreference +Version: v0.0.0-20160704190145-13c6e3589ad9 +Licence type (autodetected): Apache-2.0 +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/go-openapi/jsonreference@v0.0.0-20160704190145-13c6e3589ad9/LICENSE: + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. + + +-------------------------------------------------------------------------------- +Dependency : github.com/go-openapi/spec +Version: v0.0.0-20160808142527-6aced65f8501 +Licence type (autodetected): Apache-2.0 +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/go-openapi/spec@v0.0.0-20160808142527-6aced65f8501/LICENSE: + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. + + +-------------------------------------------------------------------------------- +Dependency : github.com/go-openapi/swag +Version: v0.0.0-20160704191624-1d0bd113de87 +Licence type (autodetected): Apache-2.0 +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/go-openapi/swag@v0.0.0-20160704191624-1d0bd113de87/LICENSE: + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. + + +-------------------------------------------------------------------------------- +Dependency : github.com/go-sourcemap/sourcemap +Version: v2.1.2+incompatible +Licence type (autodetected): BSD-2-Clause +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/go-sourcemap/sourcemap@v2.1.2+incompatible/LICENSE: + +Copyright (c) 2016 The github.com/go-sourcemap/sourcemap Contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +-------------------------------------------------------------------------------- +Dependency : github.com/go-stack/stack +Version: v1.8.0 +Licence type (autodetected): MIT +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/go-stack/stack@v1.8.0/LICENSE.md: + +The MIT License (MIT) + +Copyright (c) 2014 Chris Hines + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +-------------------------------------------------------------------------------- +Dependency : github.com/gobuffalo/here +Version: v0.6.0 +Licence type (autodetected): MIT +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/gobuffalo/here@v0.6.0/LICENSE: + +The MIT License (MIT) + +Copyright (c) 2019 Mark Bates + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +-------------------------------------------------------------------------------- +Dependency : github.com/godbus/dbus/v5 +Version: v5.0.3 +Licence type (autodetected): BSD-2-Clause +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/godbus/dbus/v5@v5.0.3/LICENSE: + +Copyright (c) 2013, Georg Reinke (), Google +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +-------------------------------------------------------------------------------- +Dependency : github.com/golang-sql/civil +Version: v0.0.0-20190719163853-cb61b32ac6fe +Licence type (autodetected): Apache-2.0 +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/golang-sql/civil@v0.0.0-20190719163853-cb61b32ac6fe/LICENSE: Apache License @@ -30822,6 +32563,370 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice the Mozilla Public License, v. 2.0. +-------------------------------------------------------------------------------- +Dependency : github.com/hashicorp/hcl +Version: v1.0.0 +Licence type (autodetected): MPL-2.0 +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/hashicorp/hcl@v1.0.0/LICENSE: + +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. “Contributor” + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. “Contributor Version” + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor’s Contribution. + +1.3. “Contribution” + + means Covered Software of a particular Contributor. + +1.4. “Covered Software” + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. “Incompatible With Secondary Licenses” + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of version + 1.1 or earlier of the License, but not also under the terms of a + Secondary License. + +1.6. “Executable Form” + + means any form of the work other than Source Code Form. + +1.7. “Larger Work” + + means a work that combines Covered Software with other material, in a separate + file or files, that is not Covered Software. + +1.8. “License” + + means this document. + +1.9. “Licensable” + + means having the right to grant, to the maximum extent possible, whether at the + time of the initial grant or subsequently, any and all of the rights conveyed by + this License. + +1.10. “Modifications” + + means any of the following: + + a. any file in Source Code Form that results from an addition to, deletion + from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. “Patent Claims” of a Contributor + + means any patent claim(s), including without limitation, method, process, + and apparatus claims, in any patent Licensable by such Contributor that + would be infringed, but for the grant of the License, by the making, + using, selling, offering for sale, having made, import, or transfer of + either its Contributions or its Contributor Version. + +1.12. “Secondary License” + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. “Source Code Form” + + means the form of the work preferred for making modifications. + +1.14. “You” (or “Your”) + + means an individual or a legal entity exercising rights under this + License. For legal entities, “You” includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, “control” means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or as + part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its Contributions + or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution become + effective for each Contribution on the date the Contributor first distributes + such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under this + License. No additional rights or licenses will be implied from the distribution + or licensing of Covered Software under this License. Notwithstanding Section + 2.1(b) above, no patent license is granted by a Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party’s + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of its + Contributions. + + This License does not grant any rights in the trademarks, service marks, or + logos of any Contributor (except as may be necessary to comply with the + notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this License + (see Section 10.2) or under the terms of a Secondary License (if permitted + under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its Contributions + are its original creation(s) or it has sufficient rights to grant the + rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under applicable + copyright doctrines of fair use, fair dealing, or other equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under the + terms of this License. You must inform recipients that the Source Code Form + of the Covered Software is governed by the terms of this License, and how + they can obtain a copy of this License. You may not attempt to alter or + restrict the recipients’ rights in the Source Code Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this License, + or sublicense it under different terms, provided that the license for + the Executable Form does not attempt to limit or alter the recipients’ + rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for the + Covered Software. If the Larger Work is a combination of Covered Software + with a work governed by one or more Secondary Licenses, and the Covered + Software is not Incompatible With Secondary Licenses, this License permits + You to additionally distribute such Covered Software under the terms of + such Secondary License(s), so that the recipient of the Larger Work may, at + their option, further distribute the Covered Software under the terms of + either this License or such Secondary License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices (including + copyright notices, patent notices, disclaimers of warranty, or limitations + of liability) contained within the Source Code Form of the Covered + Software, except that You may alter any license notices to the extent + required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on behalf + of any Contributor. You must make it absolutely clear that any such + warranty, support, indemnity, or liability obligation is offered by You + alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, judicial + order, or regulation then You must: (a) comply with the terms of this License + to the maximum extent possible; and (b) describe the limitations and the code + they affect. Such description must be placed in a text file included with all + distributions of the Covered Software under this License. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing basis, + if such Contributor fails to notify You of the non-compliance by some + reasonable means prior to 60 days after You have come back into compliance. + Moreover, Your grants from a particular Contributor are reinstated on an + ongoing basis if such Contributor notifies You of the non-compliance by + some reasonable means, this is the first time You have received notice of + non-compliance with this License from such Contributor, and You become + compliant prior to 30 days after Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, counter-claims, + and cross-claims) alleging that a Contributor Version directly or + indirectly infringes any patent, then the rights granted to You by any and + all Contributors for the Covered Software under Section 2.1 of this License + shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an “as is” basis, without + warranty of any kind, either expressed, implied, or statutory, including, + without limitation, warranties that the Covered Software is free of defects, + merchantable, fit for a particular purpose or non-infringing. The entire + risk as to the quality and performance of the Covered Software is with You. + Should any Covered Software prove defective in any respect, You (not any + Contributor) assume the cost of any necessary servicing, repair, or + correction. This disclaimer of warranty constitutes an essential part of this + License. No use of any Covered Software is authorized under this License + except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from such + party’s negligence to the extent applicable law prohibits such limitation. + Some jurisdictions do not allow the exclusion or limitation of incidental or + consequential damages, so this exclusion and limitation may not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts of + a jurisdiction where the defendant maintains its principal place of business + and such litigation shall be governed by laws of that jurisdiction, without + reference to its conflict-of-law provisions. Nothing in this Section shall + prevent a party’s ability to bring cross-claims or counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject matter + hereof. If any provision of this License is held to be unenforceable, such + provision shall be reformed only to the extent necessary to make it + enforceable. Any law or regulation which provides that the language of a + contract shall be construed against the drafter shall not be used to construe + this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version of + the License under which You originally received the Covered Software, or + under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a modified + version of this License if you rename the license and remove any + references to the name of the license steward (except to note that such + modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then +You may include the notice in a location (such as a LICENSE file in a relevant +directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - “Incompatible With Secondary Licenses” Notice + + This Source Code Form is “Incompatible + With Secondary Licenses”, as defined by + the Mozilla Public License, v. 2.0. + + + -------------------------------------------------------------------------------- Dependency : github.com/haya14busa/go-actions-toolkit Version: v0.0.0-20200105081403-ca0307860f01 @@ -31829,6 +33934,41 @@ Contents of probable licence file $GOMODCACHE/github.com/kylelemons/godebug@v1.1 limitations under the License. +-------------------------------------------------------------------------------- +Dependency : github.com/magiconair/properties +Version: v1.8.0 +Licence type (autodetected): BSD-2-Clause +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/magiconair/properties@v1.8.0/LICENSE: + +goproperties - properties file decoder for Go + +Copyright (c) 2013-2018 - Frank Schroeder + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + -------------------------------------------------------------------------------- Dependency : github.com/mailru/easyjson Version: v0.7.1 @@ -32693,7 +34833,257 @@ Contents of probable licence file $GOMODCACHE/github.com/modern-go/reflect2@v1.0 APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. + + +-------------------------------------------------------------------------------- +Dependency : github.com/morikuni/aec +Version: v1.0.0 +Licence type (autodetected): MIT +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/morikuni/aec@v1.0.0/LICENSE: + +The MIT License (MIT) + +Copyright (c) 2016 Taihei Morikuni + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +-------------------------------------------------------------------------------- +Dependency : github.com/munnerz/goautoneg +Version: v0.0.0-20120707110453-a547fc61f48d +Licence type (autodetected): BSD-3-Clause +-------------------------------------------------------------------------------- + +No licence file provided. + +-------------------------------------------------------------------------------- +Dependency : github.com/mwitkow/go-conntrack +Version: v0.0.0-20161129095857-cc309e4a2223 +Licence type (autodetected): Apache-2.0 +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/mwitkow/go-conntrack@v0.0.0-20161129095857-cc309e4a2223/LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -32701,7 +35091,7 @@ Contents of probable licence file $GOMODCACHE/github.com/modern-go/reflect2@v1.0 same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -32717,55 +35107,116 @@ Contents of probable licence file $GOMODCACHE/github.com/modern-go/reflect2@v1.0 -------------------------------------------------------------------------------- -Dependency : github.com/morikuni/aec -Version: v1.0.0 -Licence type (autodetected): MIT +Dependency : github.com/mxk/go-flowrate +Version: v0.0.0-20140419014527-cca7078d478f +Licence type (autodetected): BSD-3-Clause -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/morikuni/aec@v1.0.0/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/mxk/go-flowrate@v0.0.0-20140419014527-cca7078d478f/LICENSE: -The MIT License (MIT) +Copyright (c) 2014 The Go-FlowRate Authors. All rights reserved. -Copyright (c) 2016 Taihei Morikuni +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the + distribution. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. + * Neither the name of the go-flowrate project nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- -Dependency : github.com/munnerz/goautoneg -Version: v0.0.0-20120707110453-a547fc61f48d -Licence type (autodetected): BSD-3-Clause +Dependency : github.com/onsi/ginkgo +Version: v1.11.0 +Licence type (autodetected): MIT -------------------------------------------------------------------------------- -No licence file provided. +Contents of probable licence file $GOMODCACHE/github.com/onsi/ginkgo@v1.11.0/LICENSE: + +Copyright (c) 2013-2014 Onsi Fakhouri + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + -------------------------------------------------------------------------------- -Dependency : github.com/mwitkow/go-conntrack -Version: v0.0.0-20161129095857-cc309e4a2223 +Dependency : github.com/onsi/gomega +Version: v1.7.0 +Licence type (autodetected): MIT +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/onsi/gomega@v1.7.0/LICENSE: + +Copyright (c) 2013-2014 Onsi Fakhouri + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +-------------------------------------------------------------------------------- +Dependency : github.com/opencontainers/go-digest +Version: v1.0.0-rc1.0.20190228220655-ac19fd6e7483 Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/mwitkow/go-conntrack@v0.0.0-20161129095857-cc309e4a2223/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/opencontainers/go-digest@v1.0.0-rc1.0.20190228220655-ac19fd6e7483/LICENSE: + Apache License Version 2.0, January 2004 - http://www.apache.org/licenses/ + https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -32940,24 +35391,13 @@ Contents of probable licence file $GOMODCACHE/github.com/mwitkow/go-conntrack@v0 END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} + Copyright 2016 Docker, Inc. Licensed 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 + https://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, @@ -32967,116 +35407,17 @@ Contents of probable licence file $GOMODCACHE/github.com/mwitkow/go-conntrack@v0 -------------------------------------------------------------------------------- -Dependency : github.com/mxk/go-flowrate -Version: v0.0.0-20140419014527-cca7078d478f -Licence type (autodetected): BSD-3-Clause --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/mxk/go-flowrate@v0.0.0-20140419014527-cca7078d478f/LICENSE: - -Copyright (c) 2014 The Go-FlowRate Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the - distribution. - - * Neither the name of the go-flowrate project nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - --------------------------------------------------------------------------------- -Dependency : github.com/onsi/ginkgo -Version: v1.11.0 -Licence type (autodetected): MIT --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/onsi/ginkgo@v1.11.0/LICENSE: - -Copyright (c) 2013-2014 Onsi Fakhouri - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - --------------------------------------------------------------------------------- -Dependency : github.com/onsi/gomega -Version: v1.7.0 -Licence type (autodetected): MIT --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/onsi/gomega@v1.7.0/LICENSE: - -Copyright (c) 2013-2014 Onsi Fakhouri - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - --------------------------------------------------------------------------------- -Dependency : github.com/opencontainers/go-digest -Version: v1.0.0-rc1.0.20190228220655-ac19fd6e7483 +Dependency : github.com/opencontainers/image-spec +Version: v1.0.2-0.20190823105129-775207bd45b6 Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/opencontainers/go-digest@v1.0.0-rc1.0.20190228220655-ac19fd6e7483/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/opencontainers/image-spec@v1.0.2-0.20190823105129-775207bd45b6/LICENSE: Apache License Version 2.0, January 2004 - https://www.apache.org/licenses/ + http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -33251,13 +35592,13 @@ Contents of probable licence file $GOMODCACHE/github.com/opencontainers/go-diges END OF TERMS AND CONDITIONS - Copyright 2016 Docker, Inc. + Copyright 2016 The Linux Foundation. Licensed 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 - https://www.apache.org/licenses/LICENSE-2.0 + 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, @@ -33267,12 +35608,12 @@ Contents of probable licence file $GOMODCACHE/github.com/opencontainers/go-diges -------------------------------------------------------------------------------- -Dependency : github.com/opencontainers/image-spec -Version: v1.0.2-0.20190823105129-775207bd45b6 +Dependency : github.com/opencontainers/runc +Version: v1.0.0-rc9 Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/opencontainers/image-spec@v1.0.2-0.20190823105129-775207bd45b6/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/opencontainers/runc@v1.0.0-rc9/LICENSE: Apache License @@ -33452,7 +35793,7 @@ Contents of probable licence file $GOMODCACHE/github.com/opencontainers/image-sp END OF TERMS AND CONDITIONS - Copyright 2016 The Linux Foundation. + Copyright 2014 Docker, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -33468,12 +35809,12 @@ Contents of probable licence file $GOMODCACHE/github.com/opencontainers/image-sp -------------------------------------------------------------------------------- -Dependency : github.com/opencontainers/runc -Version: v1.0.0-rc9 +Dependency : github.com/opencontainers/runtime-spec +Version: v1.0.1 Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/opencontainers/runc@v1.0.0-rc9/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/opencontainers/runtime-spec@v1.0.1/LICENSE: Apache License @@ -33653,7 +35994,7 @@ Contents of probable licence file $GOMODCACHE/github.com/opencontainers/runc@v1. END OF TERMS AND CONDITIONS - Copyright 2014 Docker, Inc. + Copyright 2015 The Linux Foundation. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -33669,12 +36010,12 @@ Contents of probable licence file $GOMODCACHE/github.com/opencontainers/runc@v1. -------------------------------------------------------------------------------- -Dependency : github.com/opencontainers/runtime-spec -Version: v1.0.1 +Dependency : github.com/opencontainers/runtime-tools +Version: v0.0.0-20181011054405-1d69bd0f9c39 Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/opencontainers/runtime-spec@v1.0.1/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/opencontainers/runtime-tools@v0.0.0-20181011054405-1d69bd0f9c39/LICENSE: Apache License @@ -33870,12 +36211,60 @@ Contents of probable licence file $GOMODCACHE/github.com/opencontainers/runtime- -------------------------------------------------------------------------------- -Dependency : github.com/opencontainers/runtime-tools -Version: v0.0.0-20181011054405-1d69bd0f9c39 +Dependency : github.com/otiai10/curr +Version: v1.0.0 +Licence type (autodetected): MIT +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/otiai10/curr@v1.0.0/LICENSE: + +The MIT License (MIT) + +Copyright (c) 2020 Hiromu Ochiai + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +-------------------------------------------------------------------------------- +Dependency : github.com/otiai10/mint +Version: v1.3.1 +Licence type (autodetected): MIT +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/otiai10/mint@v1.3.1/LICENSE: + +Copyright 2017 otiai10 (Hiromu OCHIAI) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +-------------------------------------------------------------------------------- +Dependency : github.com/oxtoacart/bpool +Version: v0.0.0-20150712133111-4e1c5567d7c2 Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/opencontainers/runtime-tools@v0.0.0-20181011054405-1d69bd0f9c39/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/oxtoacart/bpool@v0.0.0-20150712133111-4e1c5567d7c2/LICENSE: Apache License @@ -34055,7 +36444,18 @@ Contents of probable licence file $GOMODCACHE/github.com/opencontainers/runtime- END OF TERMS AND CONDITIONS - Copyright 2015 The Linux Foundation. + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2014 Percy Wegmann Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -34071,16 +36471,45 @@ Contents of probable licence file $GOMODCACHE/github.com/opencontainers/runtime- -------------------------------------------------------------------------------- -Dependency : github.com/otiai10/curr -Version: v1.0.0 +Dependency : github.com/pelletier/go-toml +Version: v1.2.0 Licence type (autodetected): MIT -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/otiai10/curr@v1.0.0/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/pelletier/go-toml@v1.2.0/LICENSE: The MIT License (MIT) -Copyright (c) 2020 Hiromu Ochiai +Copyright (c) 2013 - 2017 Thomas Pelletier, Eric Anderton + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +-------------------------------------------------------------------------------- +Dependency : github.com/peterbourgon/diskv +Version: v2.0.1+incompatible +Licence type (autodetected): MIT +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/peterbourgon/diskv@v2.0.1+incompatible/LICENSE: + +Copyright (c) 2011-2012 Peter Bourgon Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -34102,30 +36531,81 @@ THE SOFTWARE. -------------------------------------------------------------------------------- -Dependency : github.com/otiai10/mint -Version: v1.3.1 +Dependency : github.com/pierrec/lz4 +Version: v2.4.1+incompatible +Licence type (autodetected): BSD-3-Clause +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/pierrec/lz4@v2.4.1+incompatible/LICENSE: + +Copyright (c) 2015, Pierre Curto +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of xxHash nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +-------------------------------------------------------------------------------- +Dependency : github.com/poy/eachers +Version: v0.0.0-20181020210610-23942921fe77 Licence type (autodetected): MIT -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/otiai10/mint@v1.3.1/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/poy/eachers@v0.0.0-20181020210610-23942921fe77/LICENSE.md: -Copyright 2017 otiai10 (Hiromu OCHIAI) +The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +Copyright (c) 2016 Andrew Poydence -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. -------------------------------------------------------------------------------- -Dependency : github.com/oxtoacart/bpool -Version: v0.0.0-20150712133111-4e1c5567d7c2 +Dependency : github.com/prometheus/client_golang +Version: v1.1.1-0.20190913103102-20428fa0bffc Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/oxtoacart/bpool@v0.0.0-20150712133111-4e1c5567d7c2/LICENSE: - +Contents of probable licence file $GOMODCACHE/github.com/prometheus/client_golang@v1.1.1-0.20190913103102-20428fa0bffc/LICENSE: Apache License Version 2.0, January 2004 @@ -34262,179 +36742,82 @@ Contents of probable licence file $GOMODCACHE/github.com/oxtoacart/bpool@v0.0.0- this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2014 Percy Wegmann - - Licensed 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. - - --------------------------------------------------------------------------------- -Dependency : github.com/peterbourgon/diskv -Version: v2.0.1+incompatible -Licence type (autodetected): MIT --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/peterbourgon/diskv@v2.0.1+incompatible/LICENSE: - -Copyright (c) 2011-2012 Peter Bourgon - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - --------------------------------------------------------------------------------- -Dependency : github.com/pierrec/lz4 -Version: v2.4.1+incompatible -Licence type (autodetected): BSD-3-Clause --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/pierrec/lz4@v2.4.1+incompatible/LICENSE: - -Copyright (c) 2015, Pierre Curto -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. + with Licensor regarding such Contributions. -* Neither the name of xxHash nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. --------------------------------------------------------------------------------- -Dependency : github.com/poy/eachers -Version: v0.0.0-20181020210610-23942921fe77 -Licence type (autodetected): MIT --------------------------------------------------------------------------------- + END OF TERMS AND CONDITIONS -Contents of probable licence file $GOMODCACHE/github.com/poy/eachers@v0.0.0-20181020210610-23942921fe77/LICENSE.md: + APPENDIX: How to apply the Apache License to your work. -The MIT License (MIT) + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. -Copyright (c) 2016 Andrew Poydence + Copyright [yyyy] [name of copyright owner] -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: + Licensed 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 -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. + http://www.apache.org/licenses/LICENSE-2.0 -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. + 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. -------------------------------------------------------------------------------- -Dependency : github.com/prometheus/client_golang -Version: v1.1.1-0.20190913103102-20428fa0bffc +Dependency : github.com/rakyll/statik +Version: v0.1.6 Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/prometheus/client_golang@v1.1.1-0.20190913103102-20428fa0bffc/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/rakyll/statik@v0.1.6/LICENSE: + Apache License Version 2.0, January 2004 @@ -34624,7 +37007,7 @@ Contents of probable licence file $GOMODCACHE/github.com/prometheus/client_golan same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright 2014 Google Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -34640,13 +37023,163 @@ Contents of probable licence file $GOMODCACHE/github.com/prometheus/client_golan -------------------------------------------------------------------------------- -Dependency : github.com/rakyll/statik -Version: v0.1.6 -Licence type (autodetected): Apache-2.0 +Dependency : github.com/reviewdog/errorformat +Version: v0.0.0-20200109134752-8983be9bc7dd +Licence type (autodetected): MIT -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/rakyll/statik@v0.1.6/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/reviewdog/errorformat@v0.0.0-20200109134752-8983be9bc7dd/LICENSE: + +MIT License + +Copyright (c) 2016 haya14busa + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +-------------------------------------------------------------------------------- +Dependency : github.com/rogpeppe/fastuuid +Version: v1.2.0 +Licence type (autodetected): BSD-3-Clause +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/rogpeppe/fastuuid@v1.2.0/LICENSE: + +Copyright © 2014, Roger Peppe +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of this project nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +-------------------------------------------------------------------------------- +Dependency : github.com/rogpeppe/go-internal +Version: v1.3.0 +Licence type (autodetected): BSD-3-Clause +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/rogpeppe/go-internal@v1.3.0/LICENSE: + +Copyright (c) 2018 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +-------------------------------------------------------------------------------- +Dependency : github.com/russross/blackfriday +Version: v1.5.2 +Licence type (autodetected): BSD-2-Clause +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/russross/blackfriday@v1.5.2/LICENSE.txt: + +Blackfriday is distributed under the Simplified BSD License: + +> Copyright © 2011 Russ Ross +> All rights reserved. +> +> Redistribution and use in source and binary forms, with or without +> modification, are permitted provided that the following conditions +> are met: +> +> 1. Redistributions of source code must retain the above copyright +> notice, this list of conditions and the following disclaimer. +> +> 2. Redistributions in binary form must reproduce the above +> copyright notice, this list of conditions and the following +> disclaimer in the documentation and/or other materials provided with +> the distribution. +> +> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +> "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +> LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +> FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +> COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +> INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +> BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +> LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +> CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +> LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +> ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +> POSSIBILITY OF SUCH DAMAGE. + +-------------------------------------------------------------------------------- +Dependency : github.com/samuel/go-parser +Version: v0.0.0-20130731160455-ca8abbf65d0e +Licence type (autodetected): BSD-3-Clause +-------------------------------------------------------------------------------- + +No licence file provided. + +-------------------------------------------------------------------------------- +Dependency : github.com/sanathkr/go-yaml +Version: v0.0.0-20170819195128-ed9d249f429b +Licence type (autodetected): Apache-2.0 +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/sanathkr/go-yaml@v0.0.0-20170819195128-ed9d249f429b/LICENSE: Apache License Version 2.0, January 2004 @@ -34828,7 +37361,7 @@ Contents of probable licence file $GOMODCACHE/github.com/rakyll/statik@v0.1.6/LI APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" + boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -34836,7 +37369,7 @@ Contents of probable licence file $GOMODCACHE/github.com/rakyll/statik@v0.1.6/LI same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2014 Google Inc. + Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -34852,16 +37385,16 @@ Contents of probable licence file $GOMODCACHE/github.com/rakyll/statik@v0.1.6/LI -------------------------------------------------------------------------------- -Dependency : github.com/reviewdog/errorformat -Version: v0.0.0-20200109134752-8983be9bc7dd +Dependency : github.com/sanathkr/yaml +Version: v1.0.1-0.20170819201035-0056894fa522 Licence type (autodetected): MIT -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/reviewdog/errorformat@v0.0.0-20200109134752-8983be9bc7dd/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/sanathkr/yaml@v1.0.1-0.20170819201035-0056894fa522/LICENSE: -MIT License +The MIT License (MIT) -Copyright (c) 2016 haya14busa +Copyright (c) 2014 Sam Ghods Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -34882,51 +37415,44 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------------------------------- -Dependency : github.com/rogpeppe/fastuuid -Version: v1.2.0 -Licence type (autodetected): BSD-3-Clause --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/rogpeppe/fastuuid@v1.2.0/LICENSE: - -Copyright © 2014, Roger Peppe -All rights reserved. +Copyright (c) 2012 The Go Authors. All rights reserved. -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of this project nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- -Dependency : github.com/rogpeppe/go-internal -Version: v1.3.0 +Dependency : github.com/santhosh-tekuri/jsonschema +Version: v1.2.4 Licence type (autodetected): BSD-3-Clause -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/rogpeppe/go-internal@v1.3.0/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/santhosh-tekuri/jsonschema@v1.2.4/LICENSE: -Copyright (c) 2018 The Go Authors. All rights reserved. +Copyright (c) 2017 Santhosh Kumar Tekuri. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -34954,24 +37480,206 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- +Dependency : github.com/satori/go.uuid +Version: v1.2.0 +Licence type (autodetected): MIT +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/satori/go.uuid@v1.2.0/LICENSE: + +Copyright (C) 2013-2018 by Maxim Bublis + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + -------------------------------------------------------------------------------- -Dependency : github.com/samuel/go-parser -Version: v0.0.0-20130731160455-ca8abbf65d0e +Dependency : github.com/sergi/go-diff +Version: v1.1.0 +Licence type (autodetected): MIT +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/sergi/go-diff@v1.1.0/LICENSE: + +Copyright (c) 2012-2016 The go-diff Authors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + + +-------------------------------------------------------------------------------- +Dependency : github.com/sirupsen/logrus +Version: v1.4.2 +Licence type (autodetected): MIT +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/sirupsen/logrus@v1.4.2/LICENSE: + +The MIT License (MIT) + +Copyright (c) 2014 Simon Eskildsen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +-------------------------------------------------------------------------------- +Dependency : github.com/smartystreets/assertions +Version: v0.0.0-20180927180507-b2de0cb4f26d +Licence type (autodetected): MIT +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/smartystreets/assertions@v0.0.0-20180927180507-b2de0cb4f26d/LICENSE.md: + +Copyright (c) 2016 SmartyStreets, LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +NOTE: Various optional and subordinate components carry their own licensing +requirements and restrictions. Use of those components is subject to the terms +and conditions outlined the respective license of each component. + + +-------------------------------------------------------------------------------- +Dependency : github.com/smartystreets/goconvey +Version: v0.0.0-20190330032615-68dc04aab96a +Licence type (autodetected): MIT +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/smartystreets/goconvey@v0.0.0-20190330032615-68dc04aab96a/LICENSE.md: + +Copyright (c) 2016 SmartyStreets, LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +NOTE: Various optional and subordinate components carry their own licensing +requirements and restrictions. Use of those components is subject to the terms +and conditions outlined the respective license of each component. + + +-------------------------------------------------------------------------------- +Dependency : github.com/spaolacci/murmur3 +Version: v1.1.0 Licence type (autodetected): BSD-3-Clause -------------------------------------------------------------------------------- -No licence file provided. +Contents of probable licence file $GOMODCACHE/github.com/spaolacci/murmur3@v1.1.0/LICENSE: + +Copyright 2013, Sébastien Paolacci. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the library nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + -------------------------------------------------------------------------------- -Dependency : github.com/sanathkr/go-yaml -Version: v0.0.0-20170819195128-ed9d249f429b +Dependency : github.com/spf13/afero +Version: v1.2.2 Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/sanathkr/go-yaml@v0.0.0-20170819195128-ed9d249f429b/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/spf13/afero@v1.2.2/LICENSE.txt: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -35146,45 +37854,18 @@ Contents of probable licence file $GOMODCACHE/github.com/sanathkr/go-yaml@v0.0.0 incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed 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. - -------------------------------------------------------------------------------- -Dependency : github.com/sanathkr/yaml -Version: v1.0.1-0.20170819201035-0056894fa522 +Dependency : github.com/spf13/cast +Version: v1.3.0 Licence type (autodetected): MIT -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/sanathkr/yaml@v1.0.1-0.20170819201035-0056894fa522/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/spf13/cast@v1.3.0/LICENSE: The MIT License (MIT) -Copyright (c) 2014 Sam Ghods +Copyright (c) 2014 Steve Francia Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -35204,143 +37885,17 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Copyright (c) 2012 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - --------------------------------------------------------------------------------- -Dependency : github.com/santhosh-tekuri/jsonschema -Version: v1.2.4 -Licence type (autodetected): BSD-3-Clause --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/santhosh-tekuri/jsonschema@v1.2.4/LICENSE: - -Copyright (c) 2017 Santhosh Kumar Tekuri. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -------------------------------------------------------------------------------- -Dependency : github.com/satori/go.uuid -Version: v1.2.0 -Licence type (autodetected): MIT --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/satori/go.uuid@v1.2.0/LICENSE: - -Copyright (C) 2013-2018 by Maxim Bublis - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - --------------------------------------------------------------------------------- -Dependency : github.com/sergi/go-diff -Version: v1.1.0 -Licence type (autodetected): MIT --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/sergi/go-diff@v1.1.0/LICENSE: - -Copyright (c) 2012-2016 The go-diff Authors. All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. - - - --------------------------------------------------------------------------------- -Dependency : github.com/sirupsen/logrus -Version: v1.4.2 +Dependency : github.com/spf13/jwalterweatherman +Version: v1.0.0 Licence type (autodetected): MIT -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/sirupsen/logrus@v1.4.2/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/spf13/jwalterweatherman@v1.0.0/LICENSE: The MIT License (MIT) -Copyright (c) 2014 Simon Eskildsen +Copyright (c) 2014 Steve Francia Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -35349,60 +37904,28 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - --------------------------------------------------------------------------------- -Dependency : github.com/smartystreets/assertions -Version: v0.0.0-20180927180507-b2de0cb4f26d -Licence type (autodetected): MIT --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/smartystreets/assertions@v0.0.0-20180927180507-b2de0cb4f26d/LICENSE.md: - -Copyright (c) 2016 SmartyStreets, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -NOTE: Various optional and subordinate components carry their own licensing -requirements and restrictions. Use of those components is subject to the terms -and conditions outlined the respective license of each component. - - -------------------------------------------------------------------------------- -Dependency : github.com/smartystreets/goconvey -Version: v0.0.0-20190330032615-68dc04aab96a +Dependency : github.com/spf13/viper +Version: v1.3.2 Licence type (autodetected): MIT -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/smartystreets/goconvey@v0.0.0-20190330032615-68dc04aab96a/LICENSE.md: +Contents of probable licence file $GOMODCACHE/github.com/spf13/viper@v1.3.2/LICENSE: -Copyright (c) 2016 SmartyStreets, LLC +The MIT License (MIT) + +Copyright (c) 2014 Steve Francia Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -35422,195 +37945,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -NOTE: Various optional and subordinate components carry their own licensing -requirements and restrictions. Use of those components is subject to the terms -and conditions outlined the respective license of each component. - - --------------------------------------------------------------------------------- -Dependency : github.com/spf13/afero -Version: v1.2.2 -Licence type (autodetected): Apache-2.0 --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/spf13/afero@v1.2.2/LICENSE.txt: - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - -------------------------------------------------------------------------------- Dependency : github.com/stretchr/objx Version: v0.2.0 @@ -35677,6 +38011,38 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- +Dependency : github.com/ugorji/go +Version: v1.1.8 +Licence type (autodetected): MIT +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/ugorji/go@v1.1.8/LICENSE: + +The MIT License (MIT) + +Copyright (c) 2012-2015 Ugorji Nwoke. +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + -------------------------------------------------------------------------------- Dependency : github.com/urfave/cli Version: v0.0.0-20171014202726-7bc6a0acffa5 @@ -37420,6 +39786,25 @@ Contents of probable licence file $GOMODCACHE/github.com/xeipuuv/gojsonschema@v0 limitations under the License. +-------------------------------------------------------------------------------- +Dependency : github.com/xordataexchange/crypt +Version: v0.0.3-0.20170626215501-b2862e3d0a77 +Licence type (autodetected): MIT +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/xordataexchange/crypt@v0.0.3-0.20170626215501-b2862e3d0a77/LICENSE: + +The MIT License (MIT) + +Copyright (c) 2014 XOR Data Exchange, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------------- Dependency : github.com/yuin/gopher-lua Version: v0.0.0-20170403160031-b402f3114ec7 diff --git a/go.mod b/go.mod index 09cd086cbee..bb31374384a 100644 --- a/go.mod +++ b/go.mod @@ -45,6 +45,7 @@ require ( github.com/davecgh/go-xdr v0.0.0-20161123171359-e6a2ba005892 // indirect github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e github.com/devigned/tab v0.1.2-0.20190607222403-0c15cf42f9a2 // indirect + github.com/dgraph-io/badger/v2 v2.2007.2 github.com/dgrijalva/jwt-go v3.2.1-0.20190620180102-5e25c22bd5d6+incompatible // indirect github.com/digitalocean/go-libvirt v0.0.0-20180301200012-6075ea3c39a1 github.com/dlclark/regexp2 v1.1.7-0.20171009020623-7632a260cbaf // indirect @@ -55,7 +56,7 @@ require ( github.com/docker/go-units v0.4.0 github.com/dop251/goja v0.0.0-20200831102558-9af81ddcf0e1 github.com/dop251/goja_nodejs v0.0.0-20171011081505-adff31b136e6 - github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4 + github.com/dustin/go-humanize v1.0.0 github.com/eclipse/paho.mqtt.golang v1.2.1-0.20200121105743-0d940dd29fd2 github.com/elastic/ecs v1.6.0 github.com/elastic/elastic-agent-client/v7 v7.0.0-20200709172729-d43b7ad5833a @@ -142,12 +143,13 @@ require ( github.com/satori/go.uuid v1.2.0 // indirect github.com/shirou/gopsutil v2.19.11+incompatible github.com/shopspring/decimal v1.2.0 - github.com/spf13/cobra v0.0.3 + github.com/spf13/cobra v0.0.5 github.com/spf13/pflag v1.0.5 github.com/stretchr/objx v0.2.0 // indirect github.com/stretchr/testify v1.6.1 github.com/tsg/go-daemon v0.0.0-20200207173439-e704b93fd89b github.com/tsg/gopacket v0.0.0-20200626092518-2ab8e397a786 + github.com/ugorji/go/codec v1.1.8 github.com/urso/sderr v0.0.0-20200210124243-c2a16f3d43ec github.com/vmware/govmomi v0.0.0-20170802214208-2cad15190b41 github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c diff --git a/go.sum b/go.sum index 031f1faa095..8113d42f321 100644 --- a/go.sum +++ b/go.sum @@ -76,6 +76,8 @@ github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbt github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM= +github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA= @@ -83,6 +85,8 @@ github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tT github.com/Microsoft/hcsshim v0.8.7 h1:ptnOoufxGSzauVTsdE+wMYnCWA301PdoN4xg5oRdZpg= github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= @@ -112,6 +116,7 @@ github.com/antlr/antlr4 v0.0.0-20200820155224-be881fa6b91d h1:OE3kzLBpy7pOJEzE55 github.com/antlr/antlr4 v0.0.0-20200820155224-be881fa6b91d/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y= github.com/apoydence/eachers v0.0.0-20181020210610-23942921fe77 h1:afT88tB6u9JCKQZVAAaa9ICz/uGn5Uw9ekn6P22mYKM= github.com/apoydence/eachers v0.0.0-20181020210610-23942921fe77/go.mod h1:bXvGk6IkT1Agy7qzJ+DjIw/SJ1AaB3AvAuMDVV+Vkoo= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -141,6 +146,8 @@ github.com/cavaliercoder/badio v0.0.0-20160213150051-ce5280129e9e/go.mod h1:V284 github.com/cavaliercoder/go-rpm v0.0.0-20190131055624-7a9c54e3d83e h1:Gbx+iVCXG/1m5WSnidDGuHgN+vbIwl+6fR092ANU+Y8= github.com/cavaliercoder/go-rpm v0.0.0-20190131055624-7a9c54e3d83e/go.mod h1:AZIh1CCnMrcVm6afFf96PBvE2MRpWFco91z8ObJtgDY= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -170,12 +177,16 @@ github.com/containerd/fifo v0.0.0-20190816180239-bda0ff6ed73c/go.mod h1:ODA38xgv github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.0.0 h1:XJIw/+VlJ+87J+doOxznsAWIdmWuViOVhkQamW5YV28= github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea h1:n2Ltr3SrfQlf/9nOna1DoGKxLx3qTSI8Ttl6Xrqp6mw= github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cucumber/godog v0.8.1 h1:lVb+X41I4YDreE+ibZ50bdXmySxgRviYFgKY6Aw4XE8= github.com/cucumber/godog v0.8.1/go.mod h1:vSh3r/lM+psC1BPXvdkSEuNjmXfpVqrMGYAElF6hxnA= github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg= @@ -190,9 +201,15 @@ github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e/go.mod h1:xb github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= github.com/devigned/tab v0.1.2-0.20190607222403-0c15cf42f9a2 h1:6+hM8KeYKV0Z9EIINNqIEDyyIRAcNc2FW+/TUYNmWyw= github.com/devigned/tab v0.1.2-0.20190607222403-0c15cf42f9a2/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= +github.com/dgraph-io/badger/v2 v2.2007.2 h1:EjjK0KqwaFMlPin1ajhP943VPENHJdEz1KLIegjaI3k= +github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= +github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de h1:t0UHb5vdojIDUqktM6+xJAfScFBsVpXZmqC9dsgJmeA= +github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.1-0.20190620180102-5e25c22bd5d6+incompatible h1:4jGdduO4ceTJFKf0IhgaB8NJapGqKHwC2b4xQ/cXujM= github.com/dgrijalva/jwt-go v3.2.1-0.20190620180102-5e25c22bd5d6+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/digitalocean/go-libvirt v0.0.0-20180301200012-6075ea3c39a1 h1:eG5K5GNAAHvQlFmfIuy0Ocjg5dvyX22g/KknwTpmBko= github.com/digitalocean/go-libvirt v0.0.0-20180301200012-6075ea3c39a1/go.mod h1:PRcPVAAma6zcLpFd4GZrjR/MRpood3TamjKI2m/z/Uw= github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4= @@ -213,8 +230,9 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QL github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/dop251/goja_nodejs v0.0.0-20171011081505-adff31b136e6 h1:RrkoB0pT3gnjXhL/t10BSP1mcr/0Ldea2uMyuBr2SWk= github.com/dop251/goja_nodejs v0.0.0-20171011081505-adff31b136e6/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4 h1:qk/FSDDxo05wdJH28W+p5yivv7LuLYLRXPPD8KQCtZs= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q= github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw= @@ -327,7 +345,6 @@ github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -339,7 +356,6 @@ github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFU github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= @@ -366,7 +382,6 @@ github.com/google/go-github/v29 v29.0.2 h1:opYN6Wc7DOz7Ku3Oh4l7prmkOMwEcQxpFtxdU github.com/google/go-github/v29 v29.0.2/go.mod h1:CHKiKKPHJ0REzfwc14QMklvtHwCveD0PxlMjLlzAM5E= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -403,7 +418,6 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0 h1:sBDQoHXrOlfPobnKw69FIKa1wg9qsL github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c= github.com/h2non/filetype v1.0.12 h1:yHCsIe0y2cvbDARtJhGBTD2ecvqMSTvlIcph9En/Zao= github.com/h2non/filetype v1.0.12/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= -github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce h1:prjrVgOk2Yg6w+PflHoszQNLTUh4kaByUcEWM/9uin4= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -424,6 +438,7 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.2-0.20190520140433-59383c442f7d h1:Ft6PtvobE9vwkCsuoNO5DZDbhKkKuktAlSsiOi1X5NA= github.com/hashicorp/golang-lru v0.5.2-0.20190520140433-59383c442f7d/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/haya14busa/go-actions-toolkit v0.0.0-20200105081403-ca0307860f01 h1:HiJF8Mek+I7PY0Bm+SuhkwaAZSZP83sw6rrTMrgZ0io= github.com/haya14busa/go-actions-toolkit v0.0.0-20200105081403-ca0307860f01/go.mod h1:1DWDZmeYf0LX30zscWb7K9rUMeirNeBMd5Dum+seUhc= github.com/haya14busa/go-checkstyle v0.0.0-20170303121022-5e9d09f51fa1/go.mod h1:RsN5RGgVYeXpcXNtWyztD5VIe7VNSEqpJvF2iEH7QvI= @@ -455,7 +470,6 @@ github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgb github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -478,7 +492,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3 github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -490,10 +503,10 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.2-0.20190507191818-2ff3cb3adc01 h1:EPw7R3OAyxHBCyl0oqh3lUZqS5lu3KSxzzGasE0opXQ= github.com/lib/pq v1.1.2-0.20190507191818-2ff3cb3adc01/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/magefile/mage v1.9.0 h1:t3AU2wNwehMCW97vuqQLtw6puppWXHO+O2MHo5a50XE= github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magefile/mage v1.10.0 h1:3HiXzCUY12kh9bIuyXShaVe529fJfyqoVM42o/uom2g= github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8= github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= @@ -543,7 +556,6 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.5.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -575,6 +587,7 @@ github.com/otiai10/mint v1.3.1 h1:BCmzIS3n71sGfHB5NMNDB3lHYPz8fWSkCAErHed//qc= github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 h1:CXwSGu/LYmbjEab5aMCs5usQRVBGThelUKBNnoSOuso= github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2/go.mod h1:L3UMQOThbttwfYRNFOWLLVXMhk5Lkio4GGOtw5UrxS0= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pierrec/lz4 v2.4.1+incompatible h1:mFe7ttWaflA46Mhqh+jUfjp2qTbPYxLB2/OyBppH9dg= github.com/pierrec/lz4 v2.4.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -621,6 +634,7 @@ github.com/reviewdog/reviewdog v0.9.17 h1:MKb3rlQZgkEXr3d85iqtYNITXn7gDJr2kT0Ihg github.com/reviewdog/reviewdog v0.9.17/go.mod h1:Y0yPFDTi9L5ohkoecJdgbvAhq+dUXp+zI7atqVibwKg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/samuel/go-parser v0.0.0-20130731160455-ca8abbf65d0e h1:hUGyBE/4CXRPThr4b6kt+f1CN90no4Fs5CNrYOKYSIg= github.com/samuel/go-parser v0.0.0-20130731160455-ca8abbf65d0e/go.mod h1:Sb6li54lXV0yYEjI4wX8cucdQ9gqUJV3+Ngg3l9g30I= github.com/samuel/go-thrift v0.0.0-20140522043831-2187045faa54 h1:jbchLJWyhKcmOjkbC4zDvT/n5EEd7g6hnnF760rEyRA= @@ -650,14 +664,22 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykE github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= @@ -666,9 +688,7 @@ github.com/stretchr/testify v1.1.5-0.20170601210322-f6abca593680/go.mod h1:a8OnR github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.0 h1:DMOzIV76tmoDNE9pX6RSN0aDtCYeCg5VueieJaAo1uw= github.com/stretchr/testify v1.5.0/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -677,6 +697,11 @@ github.com/tsg/go-daemon v0.0.0-20200207173439-e704b93fd89b h1:X/8hkb4rQq3+QuOxp github.com/tsg/go-daemon v0.0.0-20200207173439-e704b93fd89b/go.mod h1:jAqhj/JBVC1PwcLTWd6rjQyGyItxxrhpiBl8LSuAGmw= github.com/tsg/gopacket v0.0.0-20200626092518-2ab8e397a786 h1:B/IVHYiI0d04dudYw+CvCAGqSMq8d0yWy56eD6p85BQ= github.com/tsg/gopacket v0.0.0-20200626092518-2ab8e397a786/go.mod h1:RIkfovP3Y7my19aXEjjbNd9E5TlHozzAyt7B8AaEcwg= +github.com/ugorji/go v1.1.8 h1:/D9x7IRpfMHDlizVOgxrag5Fh+/NY+LtI8bsr+AswRA= +github.com/ugorji/go v1.1.8/go.mod h1:0lNM99SwWUIRhCXnigEMClngXBk/EmpTXa7mgiewYWA= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v1.1.8 h1:4dryPvxMP9OtkjIbuNeK2nb27M38XMHLGlfNSNph/5s= +github.com/ugorji/go/codec v1.1.8/go.mod h1:X00B19HDtwvKbQY2DcYjvZxKQp8mzrJoQ6EgoIY/D2E= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urso/diag v0.0.0-20200210123136-21b3cc8eb797 h1:OHNw/6pXODJAB32NujjdQO/KIYQ3KAbHQfCzH81XdCs= github.com/urso/diag v0.0.0-20200210123136-21b3cc8eb797/go.mod h1:pNWFTeQ+V1OYT/TzWpnWb6eQBdoXpdx+H+lrH97/Oyo= @@ -705,9 +730,9 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1: github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xeipuuv/gojsonschema v0.0.0-20181112162635-ac52e6811b56 h1:yhqBHs09SmmUoNOHc9jgK4a60T3XFRtPAkYxVnqgY50= github.com/xeipuuv/gojsonschema v0.0.0-20181112162635-ac52e6811b56/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/gopher-lua v0.0.0-20170403160031-b402f3114ec7 h1:0gYLpmzecnaDCoeWxSfEJ7J1b6B/67+NV++4HKQXx+Y= github.com/yuin/gopher-lua v0.0.0-20170403160031-b402f3114ec7/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU= -go.elastic.co/apm v1.7.2 h1:0nwzVIPp4PDBXSYYtN19+1W5V+sj+C25UjqxDVoKcA8= go.elastic.co/apm v1.7.2/go.mod h1:tCw6CkOJgkWnzEthFN9HUP1uL3Gjc/Ur6m7gRPLaoH0= go.elastic.co/apm v1.8.1-0.20200909061013-2aef45b9cf4b h1:Sf+V3eV91ZuXjF3824SABFgXU+z4ZEuIX5ikDvt2lCE= go.elastic.co/apm v1.8.1-0.20200909061013-2aef45b9cf4b/go.mod h1:qoOSi09pnzJDh5fKnfY7bPmQgl8yl2tULdOu03xhui0= @@ -717,7 +742,6 @@ go.elastic.co/apm/module/apmhttp v1.7.2 h1:2mRh7SwBuEVLmJlX+hsMdcSg9xaielCLElaPn go.elastic.co/apm/module/apmhttp v1.7.2/go.mod h1:sTFWiWejnhSdZv6+dMgxGec2Nxe/ZKfHfz/xtRM+cRY= go.elastic.co/ecszap v0.1.1-0.20200424093508-cdd95a104193 h1:NjYJ/beChqugXSavTkH5tF6shvr/is8jdgJ331wfwT8= go.elastic.co/ecszap v0.1.1-0.20200424093508-cdd95a104193/go.mod h1:HTUi+QRmr3EuZMqxPX+5fyOdMNfUu5iPebgfhgsTJYQ= -go.elastic.co/fastjson v1.0.0 h1:ooXV/ABvf+tBul26jcVViPT3sBir0PvXgibYB1IQQzg= go.elastic.co/fastjson v1.0.0/go.mod h1:PmeUOMMtLHQr9ZS9J9owrAVg0FkaZDRZJEFTTGHtchs= go.elastic.co/fastjson v1.1.0 h1:3MrGBWWVIxe/xvsbpghtkFoPciPhOCmjsR/HfwEeQR4= go.elastic.co/fastjson v1.1.0/go.mod h1:boNGISWMjQsUPy/t6yqt2/1Wx4YNPSe+mZjlyw9vKKI= @@ -741,6 +765,7 @@ go.uber.org/zap v1.14.0 h1:/pduUoebOeeJzTDFuoMgC6nRkiasr1sBCIEorly7m4o= go.uber.org/zap v1.14.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -767,7 +792,6 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367 h1:0IiAsCRByjO2QjX7ZPkw5oU9x+n1YqRL802rjC0c3Aw= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -801,7 +825,6 @@ golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -818,7 +841,6 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -827,6 +849,7 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -840,6 +863,7 @@ golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190529164535-6a60838ec259/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -848,12 +872,9 @@ golang.org/x/sys v0.0.0-20191025021431-6c3a3bfe00ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200102141924-c96a22e43c9c h1:OYFUffxXPezb7BVTx9AaD4Vl0qtxmklBIkwCKH1YwDY= golang.org/x/sys v0.0.0-20200102141924-c96a22e43c9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e h1:LwyF2AFISC9nVbS6MgzsaQNSUsRXI49GS+YQ5KX/QH0= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -904,7 +925,6 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= @@ -916,7 +936,6 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= -gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -948,7 +967,6 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/libbeat/tests/system/test_cmd_completion.py b/libbeat/tests/system/test_cmd_completion.py index 2e523c0831d..a06259a5046 100644 --- a/libbeat/tests/system/test_cmd_completion.py +++ b/libbeat/tests/system/test_cmd_completion.py @@ -17,7 +17,7 @@ def test_bash_completion(self): def test_zsh_completion(self): exit_code = self.run_beat(extra_args=["completion", "zsh"]) assert exit_code == 0 - assert self.log_contains("#compdef mockbeat") + assert self.log_contains("#compdef _mockbeat mockbeat") def test_unknown_completion(self): exit_code = self.run_beat(extra_args=["completion", "awesomeshell"]) diff --git a/x-pack/libbeat/common/cloudfoundry/cache.go b/x-pack/libbeat/common/cloudfoundry/cache.go index 22f41f3b23c..0f19818666e 100644 --- a/x-pack/libbeat/common/cloudfoundry/cache.go +++ b/x-pack/libbeat/common/cloudfoundry/cache.go @@ -5,13 +5,16 @@ package cloudfoundry import ( + "crypto/sha1" + "encoding/base64" "fmt" "time" "github.com/cloudfoundry-community/go-cfclient" + "github.com/pkg/errors" - "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/logp" + "github.com/elastic/beats/v7/x-pack/libbeat/persistentcache" ) // cfClient interface is provided so unit tests can mock the actual client. @@ -22,65 +25,113 @@ type cfClient interface { // clientCacheWrap wraps the cloudfoundry client to add a cache in front of GetAppByGuid. type clientCacheWrap struct { - cache *common.Cache + cache *persistentcache.PersistentCache client cfClient log *logp.Logger errorTTL time.Duration } // newClientCacheWrap creates a new cache for application data. -func newClientCacheWrap(client cfClient, ttl time.Duration, errorTTL time.Duration, log *logp.Logger) *clientCacheWrap { +func newClientCacheWrap(client cfClient, cacheName string, ttl time.Duration, errorTTL time.Duration, log *logp.Logger) (*clientCacheWrap, error) { + options := persistentcache.Options{ + Timeout: ttl, + } + + name := "cloudfoundry" + if cacheName != "" { + name = name + "-" + sanitizeCacheName(cacheName) + } + + cache, err := persistentcache.New(name, options) + if err != nil { + return nil, fmt.Errorf("creating metadata cache: %w", err) + } + return &clientCacheWrap{ - cache: common.NewCacheWithExpireOnAdd(ttl, 100), + cache: cache, client: client, errorTTL: errorTTL, log: log, - } + }, nil } type appResponse struct { - app *cfclient.App - err error + App AppMeta `json:"a"` + Error cfclient.CloudFoundryError `json:"e,omitempty"` + ErrorMessage string `json:"em,omitempty"` +} + +func (r *appResponse) fromStructs(app cfclient.App, err error) { + if err != nil { + cause := errors.Cause(err) + if cferr, ok := cause.(cfclient.CloudFoundryError); ok { + r.Error = cferr + } + r.ErrorMessage = err.Error() + return + } + r.App = AppMeta{ + Name: app.Name, + Guid: app.Guid, + SpaceName: app.SpaceData.Entity.Name, + SpaceGuid: app.SpaceData.Meta.Guid, + OrgName: app.SpaceData.Entity.OrgData.Entity.Name, + OrgGuid: app.SpaceData.Entity.OrgData.Meta.Guid, + } +} + +func (r *appResponse) toStructs() (*AppMeta, error) { + var empty cfclient.CloudFoundryError + if r.Error != empty { + // Wrapping the error so cfclient.IsAppNotFoundError can identify it + return nil, errors.Wrap(r.Error, r.ErrorMessage) + } + if len(r.ErrorMessage) > 0 { + return nil, errors.New(r.ErrorMessage) + } + return &r.App, nil } // fetchApp uses the cfClient to retrieve an App entity and // stores it in the internal cache -func (c *clientCacheWrap) fetchAppByGuid(guid string) (*cfclient.App, error) { +func (c *clientCacheWrap) fetchAppByGuid(guid string) (*AppMeta, error) { app, err := c.client.GetAppByGuid(guid) - resp := appResponse{ - app: &app, - err: err, - } + var resp appResponse + resp.fromStructs(app, err) timeout := time.Duration(0) if err != nil { // Cache nil, because is what we want to return when there was an error - resp.app = nil timeout = c.errorTTL } - c.cache.PutWithTimeout(guid, &resp, timeout) - return resp.app, resp.err + err = c.cache.PutWithTimeout(guid, resp, timeout) + if err != nil { + return nil, fmt.Errorf("storing app response in cache: %w", err) + } + return resp.toStructs() } // GetApp returns CF Application info, either from the cache or // using the CF client. -func (c *clientCacheWrap) GetAppByGuid(guid string) (*cfclient.App, error) { - cachedResp := c.cache.Get(guid) - if cachedResp == nil { +func (c *clientCacheWrap) GetAppByGuid(guid string) (*AppMeta, error) { + var resp appResponse + err := c.cache.Get(guid, &resp) + if err != nil { return c.fetchAppByGuid(guid) } - resp, ok := cachedResp.(*appResponse) - if !ok { - return nil, fmt.Errorf("error converting cached app response (of type %T), this is likely a bug", cachedResp) - } - return resp.app, resp.err + return resp.toStructs() } -// StartJanitor starts a goroutine that will periodically clean the applications cache. -func (c *clientCacheWrap) StartJanitor(interval time.Duration) { - c.cache.StartJanitor(interval) +// Close release resources associated with this client +func (c *clientCacheWrap) Close() error { + err := c.cache.Close() + if err != nil { + return fmt.Errorf("closing cache: %w", err) + } + return nil } -// StopJanitor stops the goroutine that periodically clean the applications cache. -func (c *clientCacheWrap) StopJanitor() { - c.cache.StopJanitor() +// sanitizeCacheName returns a unique string that can be used safely as part of a file name +func sanitizeCacheName(name string) string { + hash := sha1.Sum([]byte(name)) + return base64.RawURLEncoding.EncodeToString(hash[:]) } diff --git a/x-pack/libbeat/common/cloudfoundry/cache_integration_test.go b/x-pack/libbeat/common/cloudfoundry/cache_integration_test.go index f6af11787c9..f799cc0615f 100644 --- a/x-pack/libbeat/common/cloudfoundry/cache_integration_test.go +++ b/x-pack/libbeat/common/cloudfoundry/cache_integration_test.go @@ -31,7 +31,7 @@ func TestGetApps(t *testing.T) { client, err := hub.Client() require.NoError(t, err) - apps, err := client.(*clientCacheWrap).client.(*cfclient.Client).ListApps() + apps, err := client.ListApps() require.NoError(t, err) t.Logf("%d applications available", len(apps)) @@ -40,8 +40,9 @@ func TestGetApps(t *testing.T) { if len(apps) == 0 { t.Skip("no apps in account?") } - client, err := hub.Client() + client, err := hub.ClientWithCache() require.NoError(t, err) + defer client.Close() guid := apps[0].Guid app, err := client.GetAppByGuid(guid) @@ -50,13 +51,15 @@ func TestGetApps(t *testing.T) { }) t.Run("handle error when application is not available", func(t *testing.T) { - client, err := hub.Client() + client, err := hub.ClientWithCache() require.NoError(t, err) + defer client.Close() testNotExists := func(t *testing.T) { app, err := client.GetAppByGuid("notexists") assert.Nil(t, app) - assert.True(t, cfclient.IsAppNotFoundError(err)) + assert.Error(t, err) + assert.True(t, cfclient.IsAppNotFoundError(err), "Error found: %v", err) } var firstTimeDuration time.Duration diff --git a/x-pack/libbeat/common/cloudfoundry/cache_test.go b/x-pack/libbeat/common/cloudfoundry/cache_test.go index 9e18a5ac86e..b345beff226 100644 --- a/x-pack/libbeat/common/cloudfoundry/cache_test.go +++ b/x-pack/libbeat/common/cloudfoundry/cache_test.go @@ -13,19 +13,25 @@ import ( "github.com/cloudfoundry-community/go-cfclient" "github.com/gofrs/uuid" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/elastic/beats/v7/libbeat/logp" ) func TestClientCacheWrap(t *testing.T) { - ttl := 500 * time.Millisecond + if testing.Short() { + t.Skip("skipping in short mode") + } + + ttl := 2 * time.Second guid := mustCreateFakeGuid() app := cfclient.App{ - Guid: guid, - Memory: 1, // use this field to track if from cache or from client + Guid: guid, + Name: "Foo", // use this field to track if from cache or from client } fakeClient := &fakeCFClient{app, 0} - cache := newClientCacheWrap(fakeClient, ttl, ttl, logp.NewLogger("cloudfoundry")) + cache, err := newClientCacheWrap(fakeClient, "test", ttl, ttl, logp.NewLogger("cloudfoundry")) + require.NoError(t, err) missingAppGuid := mustCreateFakeGuid() @@ -44,25 +50,28 @@ func TestClientCacheWrap(t *testing.T) { // fetched from client for the first time one, err = cache.GetAppByGuid(guid) assert.NoError(t, err) - assert.Equal(t, app, *one) + assert.Equal(t, app.Guid, one.Guid) + assert.Equal(t, app.Name, one.Name) assert.Equal(t, 2, fakeClient.callCount) // updated app in fake client, new fetch should not have updated app updatedApp := cfclient.App{ - Guid: guid, - Memory: 2, + Guid: guid, + Name: "Bar", } fakeClient.app = updatedApp two, err := cache.GetAppByGuid(guid) assert.NoError(t, err) - assert.Equal(t, app, *two) + assert.Equal(t, app.Guid, two.Guid) + assert.Equal(t, app.Name, two.Name) assert.Equal(t, 2, fakeClient.callCount) // wait the ttl, then it should have updated app time.Sleep(ttl) three, err := cache.GetAppByGuid(guid) assert.NoError(t, err) - assert.Equal(t, updatedApp, *three) + assert.Equal(t, updatedApp.Guid, three.Guid) + assert.Equal(t, updatedApp.Name, three.Name) assert.Equal(t, 3, fakeClient.callCount) } diff --git a/x-pack/libbeat/common/cloudfoundry/hub.go b/x-pack/libbeat/common/cloudfoundry/hub.go index 4bb7fce1eec..4cf9757c278 100644 --- a/x-pack/libbeat/common/cloudfoundry/hub.go +++ b/x-pack/libbeat/common/cloudfoundry/hub.go @@ -5,10 +5,8 @@ package cloudfoundry import ( - "fmt" "net/http" "strings" - "time" "github.com/cloudfoundry-community/go-cfclient" "github.com/pkg/errors" @@ -19,11 +17,20 @@ import ( // Client interface exposed by Hub.Client. type Client interface { // GetAppByGuid returns the application from cloudfoundry. - GetAppByGuid(guid string) (*cfclient.App, error) - // StartJanitor keeps the cache of applications clean. - StartJanitor(interval time.Duration) - // StopJanitor stops the running janitor. - StopJanitor() + GetAppByGuid(guid string) (*AppMeta, error) + + // Close releases resources associated with this client. + Close() error +} + +// AppMeta is the metadata associated with a cloudfoundry application +type AppMeta struct { + Guid string `json:"guid"` + Name string `json:"name"` + SpaceGuid string `json:"space_guid"` + SpaceName string `json:"space_name"` + OrgGuid string `json:"org_guid"` + OrgName string `json:"org_name"` } // Hub is central place to get all the required clients to communicate with cloudfoundry. @@ -39,7 +46,7 @@ func NewHub(cfg *Config, userAgent string, log *logp.Logger) *Hub { } // Client returns the cloudfoundry client. -func (h *Hub) Client() (Client, error) { +func (h *Hub) Client() (*cfclient.Client, error) { httpClient, insecure, err := h.httpClient() if err != nil { return nil, err @@ -67,7 +74,15 @@ func (h *Hub) Client() (Client, error) { if h.cfg.UaaAddress != "" { cf.Endpoint.AuthEndpoint = h.cfg.UaaAddress } - return newClientCacheWrap(cf, h.cfg.CacheDuration, h.cfg.CacheRetryDelay, h.log), nil + return cf, nil +} + +func (h *Hub) ClientWithCache() (Client, error) { + c, err := h.Client() + if err != nil { + return nil, err + } + return newClientCacheWrap(c, h.cfg.APIAddress, h.cfg.CacheDuration, h.cfg.CacheRetryDelay, h.log) } // RlpListener returns a listener client that calls the passed callback when the provided events are streamed through @@ -85,7 +100,7 @@ func (h *Hub) RlpListener(callbacks RlpListenerCallbacks) (*RlpListener, error) // // In the case that the cloudfoundry client was already needed by the code path, call this method // as not to create a intermediate client that will not be used. -func (h *Hub) RlpListenerFromClient(client Client, callbacks RlpListenerCallbacks) (*RlpListener, error) { +func (h *Hub) RlpListenerFromClient(client *cfclient.Client, callbacks RlpListenerCallbacks) (*RlpListener, error) { var rlpAddress string if h.cfg.RlpAddress != "" { rlpAddress = h.cfg.RlpAddress @@ -107,47 +122,29 @@ func (h *Hub) DopplerConsumer(callbacks DopplerCallbacks) (*DopplerConsumer, err return h.DopplerConsumerFromClient(client, callbacks) } -func (h *Hub) DopplerConsumerFromClient(client Client, callbacks DopplerCallbacks) (*DopplerConsumer, error) { +func (h *Hub) DopplerConsumerFromClient(client *cfclient.Client, callbacks DopplerCallbacks) (*DopplerConsumer, error) { dopplerAddress := h.cfg.DopplerAddress if dopplerAddress == "" { - endpoint, err := cfEndpoint(client) - if err != nil { - return nil, errors.Wrap(err, "getting endpoints from client") - } - dopplerAddress = endpoint.DopplerEndpoint + dopplerAddress = client.Endpoint.DopplerEndpoint } httpClient, _, err := h.httpClient() if err != nil { return nil, errors.Wrap(err, "getting http client") } - // TODO: Refactor Client so it is easier to access the cfclient - ccw, ok := client.(*clientCacheWrap) - if !ok { - return nil, fmt.Errorf("client without cache wrap") - } - cfc, ok := ccw.client.(*cfclient.Client) - if !ok { - return nil, fmt.Errorf("client is not a cloud foundry client") - } - - tr := TokenRefresherFromCfClient(cfc) + tr := TokenRefresherFromCfClient(client) return newDopplerConsumer(dopplerAddress, h.cfg.ShardID, h.log, httpClient, tr, callbacks) } // doerFromClient returns an auth token doer using uaa. -func (h *Hub) doerFromClient(client Client) (*authTokenDoer, error) { +func (h *Hub) doerFromClient(client *cfclient.Client) (*authTokenDoer, error) { httpClient, _, err := h.httpClient() if err != nil { return nil, err } url := h.cfg.UaaAddress if url == "" { - endpoint, err := cfEndpoint(client) - if err != nil { - return nil, errors.Wrap(err, "getting endpoints from client") - } - url = endpoint.AuthEndpoint + url = client.Endpoint.AuthEndpoint } return newAuthTokenDoer(url, h.cfg.ClientID, h.cfg.ClientSecret, httpClient, h.log), nil } @@ -165,18 +162,6 @@ func (h *Hub) httpClient() (*http.Client, bool, error) { return httpClient, tls.InsecureSkipVerify, nil } -func cfEndpoint(client Client) (cfclient.Endpoint, error) { - ccw, ok := client.(*clientCacheWrap) - if !ok { - return cfclient.Endpoint{}, fmt.Errorf("client without cache wrap") - } - cfc, ok := ccw.client.(*cfclient.Client) - if !ok { - return cfclient.Endpoint{}, fmt.Errorf("client is not a cloud foundry client") - } - return cfc.Endpoint, nil -} - // defaultTransport returns a new http.Transport for http.Client func defaultTransport() *http.Transport { defaultTransport := http.DefaultTransport.(*http.Transport) diff --git a/x-pack/libbeat/common/cloudfoundry/main_test.go b/x-pack/libbeat/common/cloudfoundry/main_test.go new file mode 100644 index 00000000000..ec352def825 --- /dev/null +++ b/x-pack/libbeat/common/cloudfoundry/main_test.go @@ -0,0 +1,29 @@ +// 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 cloudfoundry + +import ( + "fmt" + "io/ioutil" + "os" + "testing" + + "github.com/elastic/beats/v7/libbeat/paths" +) + +func TestMain(m *testing.M) { + // Override global beats data dir to avoid creating directories in the working copy. + tmpdir, err := ioutil.TempDir("", "beats-data-dir") + if err != nil { + fmt.Printf("Failed to create temporal data directory: %v\n", err) + os.Exit(1) + } + paths.Paths.Data = tmpdir + + result := m.Run() + os.RemoveAll(tmpdir) + + os.Exit(result) +} diff --git a/x-pack/libbeat/persistentcache/encoding.go b/x-pack/libbeat/persistentcache/encoding.go new file mode 100644 index 00000000000..60b2058ebf4 --- /dev/null +++ b/x-pack/libbeat/persistentcache/encoding.go @@ -0,0 +1,55 @@ +// 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 persistentcache + +import ( + "bytes" + "encoding/json" + + ugorjicodec "github.com/ugorji/go/codec" +) + +type codec interface { + Decode([]byte, interface{}) error + Encode(interface{}) ([]byte, error) +} + +type jsonCodec struct{} + +func newJSONCodec() *jsonCodec { + return &jsonCodec{} +} + +// Encode encodes an object in json format. +func (*jsonCodec) Encode(v interface{}) ([]byte, error) { + return json.Marshal(v) +} + +// Decode decodes an object from its json representation. +func (*jsonCodec) Decode(d []byte, v interface{}) error { + return json.Unmarshal(d, v) +} + +type cborCodec struct { + handle ugorjicodec.CborHandle +} + +func newCBORCodec() *cborCodec { + return &cborCodec{} +} + +// Encode encodes an object in cbor format. +func (c *cborCodec) Encode(v interface{}) ([]byte, error) { + var buf bytes.Buffer + enc := ugorjicodec.NewEncoder(&buf, &c.handle) + err := enc.Encode(v) + return buf.Bytes(), err +} + +// Decode decodes an object from its cbor representation. +func (c *cborCodec) Decode(d []byte, v interface{}) error { + dec := ugorjicodec.NewDecoder(bytes.NewReader(d), &c.handle) + return dec.Decode(v) +} diff --git a/x-pack/libbeat/persistentcache/persistentcache.go b/x-pack/libbeat/persistentcache/persistentcache.go new file mode 100644 index 00000000000..39721095b86 --- /dev/null +++ b/x-pack/libbeat/persistentcache/persistentcache.go @@ -0,0 +1,112 @@ +// 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 persistentcache + +import ( + "fmt" + "time" + + "github.com/elastic/beats/v7/libbeat/logp" + "github.com/elastic/beats/v7/libbeat/paths" +) + +const ( + cacheFile = "cache" + gcPeriod = 5 * time.Minute +) + +// PersistentCache is a persistent map of keys to values. Elements added to the +// cache are stored until they are explicitly deleted or are expired due to time-based +// eviction based on last access or add time. +type PersistentCache struct { + log *logp.Logger + + store *Store + codec codec + + refreshOnAccess bool + timeout time.Duration +} + +// Options are the options that can be used to customize persistent caches +type Options struct { + // Length of time before cache elements expire + Timeout time.Duration + + // If set to true, expiration time of an entry is updated + // when the object is accessed. + RefreshOnAccess bool + + // If empty, beats data path is used. + RootPath string +} + +// New creates and returns a new persistent cache. +// Cache returned by this method must be closed with Close() when +// not needed anymore. +func New(name string, opts Options) (*PersistentCache, error) { + logger := logp.NewLogger("persistentcache") + + rootPath := opts.RootPath + if rootPath == "" { + rootPath = paths.Resolve(paths.Data, cacheFile) + } + store, err := newStore(logger, rootPath, name) + if err != nil { + return nil, err + } + + return &PersistentCache{ + log: logger, + store: store, + codec: newCBORCodec(), + + refreshOnAccess: opts.RefreshOnAccess, + timeout: opts.Timeout, + }, nil +} + +// Put writes the given key and value to the map replacing any +// existing value if it exists. +func (c *PersistentCache) Put(k string, v interface{}) error { + return c.PutWithTimeout(k, v, 0) +} + +// PutWithTimeout writes the given key and value to the map replacing any +// existing value if it exists. +// The cache expiration time will be overwritten by timeout of the key being +// inserted. +func (c *PersistentCache) PutWithTimeout(k string, v interface{}, timeout time.Duration) error { + d, err := c.codec.Encode(v) + if err != nil { + return fmt.Errorf("encoding item to store in cache: %w", err) + } + if timeout == 0 { + timeout = c.timeout + } + return c.store.Set([]byte(k), d, timeout) +} + +// Get the current value associated with a key or nil if the key is not +// present. The last access time of the element is updated. +func (c *PersistentCache) Get(k string, v interface{}) error { + d, err := c.store.Get([]byte(k)) + if err != nil { + return err + } + if c.refreshOnAccess && c.timeout > 0 { + c.store.Set([]byte(k), d, c.timeout) + } + err = c.codec.Decode(d, v) + if err != nil { + return fmt.Errorf("decoding item stored in cache: %w", err) + } + return nil +} + +// Close releases all resources associated with this cache. +func (c *PersistentCache) Close() error { + return c.store.Close() +} diff --git a/x-pack/libbeat/persistentcache/persistentcache_test.go b/x-pack/libbeat/persistentcache/persistentcache_test.go new file mode 100644 index 00000000000..f6f28d73b75 --- /dev/null +++ b/x-pack/libbeat/persistentcache/persistentcache_test.go @@ -0,0 +1,436 @@ +// 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 persistentcache + +import ( + "fmt" + "io/ioutil" + "math/rand" + "os" + "path/filepath" + "strconv" + "testing" + "time" + + "github.com/gofrs/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/elastic/beats/v7/libbeat/logp" +) + +func TestPutGet(t *testing.T) { + logp.TestingSetup() + t.Parallel() + + cache, err := New("test", testOptions(t)) + require.NoError(t, err) + defer cache.Close() + + type valueType struct { + Something string + } + + var key = "somekey" + var value = valueType{Something: "foo"} + + err = cache.Put(key, value) + assert.NoError(t, err) + + var result valueType + err = cache.Get(key, &result) + assert.NoError(t, err) + assert.Equal(t, value, result) + + err = cache.Get("notexist", &result) + assert.Error(t, err) +} + +func TestPersist(t *testing.T) { + logp.TestingSetup() + t.Parallel() + + options := testOptions(t) + + cache, err := New("test", options) + require.NoError(t, err) + + type valueType struct { + Something string + } + + var key = "somekey" + var value = valueType{Something: "foo"} + + err = cache.Put(key, value) + assert.NoError(t, err) + + err = cache.Close() + assert.NoError(t, err) + + cache, err = New("test", options) + require.NoError(t, err) + defer cache.Close() + + var result valueType + err = cache.Get(key, &result) + assert.NoError(t, err) + assert.Equal(t, value, result) +} + +func TestExpired(t *testing.T) { + if testing.Short() { + t.Skip("skipping in short mode") + } + logp.TestingSetup() + t.Parallel() + + options := testOptions(t) + cache, err := New("test", options) + require.NoError(t, err) + defer cache.Close() + + type valueType struct { + Something string + } + + var key = "somekey" + var value = valueType{Something: "foo"} + + // Badger TTL is not reliable on sub-second durations. + err = cache.PutWithTimeout(key, value, 2*time.Second) + assert.NoError(t, err) + + var result valueType + err = cache.Get(key, &result) + assert.NoError(t, err) + assert.Equal(t, value, result) + + time.Sleep(2 * time.Second) + err = cache.Get(key, &result) + assert.Error(t, err) +} + +func TestRefreshOnAccess(t *testing.T) { + t.Skip("flaky test") + + if testing.Short() { + t.Skip("skipping in short mode") + } + + logp.TestingSetup() + t.Parallel() + + // Badger TTL is not reliable on sub-second durations. + options := testOptions(t) + options.Timeout = 2 * time.Second + options.RefreshOnAccess = true + + cache, err := New("test", options) + require.NoError(t, err) + defer cache.Close() + + type valueType struct { + Something string + } + + var key1 = "somekey" + var value1 = valueType{Something: "foo"} + var key2 = "otherkey" + var value2 = valueType{Something: "bar"} + + err = cache.Put(key1, value1) + assert.NoError(t, err) + err = cache.Put(key2, value2) + assert.NoError(t, err) + + time.Sleep(1 * time.Second) + + var result valueType + err = cache.Get(key1, &result) + assert.NoError(t, err) + assert.Equal(t, value1, result) + + time.Sleep(1 * time.Second) + + err = cache.Get(key1, &result) + assert.NoError(t, err) + assert.Equal(t, value1, result) + err = cache.Get(key2, &result) + assert.Error(t, err) +} + +var benchmarkCacheSizes = []int{10, 100, 1000, 10000, 100000} + +func BenchmarkPut(b *testing.B) { + type cache interface { + Put(key string, value interface{}) error + Close() error + } + + options := testOptions(b) + newPersistentCache := func(tb testing.TB, name string) cache { + cache, err := New(name, options) + require.NoError(tb, err) + return cache + } + + caches := []struct { + name string + factory func(t testing.TB, name string) cache + }{ + {name: "badger", factory: newPersistentCache}, + } + + b.Run("random strings", func(b *testing.B) { + for _, c := range caches { + b.Run(c.name, func(b *testing.B) { + b.ReportAllocs() + + cache := c.factory(b, b.Name()) + defer cache.Close() + + value := uuid.Must(uuid.NewV4()).String() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := cache.Put(strconv.Itoa(i), value) + if err != nil { + b.Fatal(err) + } + } + b.StopTimer() + }) + } + }) + + b.Run("objects", func(b *testing.B) { + for _, c := range caches { + type entry struct { + ID string + Data [128]byte + } + + b.Run(c.name, func(b *testing.B) { + b.ReportAllocs() + + cache := c.factory(b, b.Name()) + defer cache.Close() + + value := entry{ID: uuid.Must(uuid.NewV4()).String()} + + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := cache.Put(strconv.Itoa(i), value) + if err != nil { + b.Fatal(err) + } + } + b.StopTimer() + }) + } + }) + + b.Run("maps", func(b *testing.B) { + for _, c := range caches { + b.Run(c.name, func(b *testing.B) { + b.ReportAllocs() + + cache := c.factory(b, b.Name()) + defer cache.Close() + + value := map[string]string{ + "id": uuid.Must(uuid.NewV4()).String(), + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := cache.Put(strconv.Itoa(i), value) + if err != nil { + b.Fatal(err) + } + } + b.StopTimer() + }) + } + }) + + for _, size := range benchmarkCacheSizes { + b.Run(fmt.Sprintf("%d objects", size), func(b *testing.B) { + type entry struct { + ID string + Data [128]byte + } + objects := make([]entry, size) + for i := 0; i < size; i++ { + objects[i] = entry{ + ID: uuid.Must(uuid.NewV4()).String(), + } + } + + for _, c := range caches { + b.Run(c.name, func(b *testing.B) { + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + cache := c.factory(b, b.Name()) + for _, object := range objects { + cache.Put(object.ID, object) + } + cache.Close() + } + }) + } + }) + } +} + +func BenchmarkOpen(b *testing.B) { + type cache interface { + Put(key string, value interface{}) error + Close() error + } + + options := testOptions(b) + newPersistentCache := func(tb testing.TB, name string) cache { + cache, err := New(name, options) + require.NoError(tb, err) + return cache + } + + caches := []struct { + name string + factory func(t testing.TB, name string) cache + }{ + {name: "badger", factory: newPersistentCache}, + } + + for _, size := range benchmarkCacheSizes { + b.Run(fmt.Sprintf("%d objects", size), func(b *testing.B) { + type entry struct { + ID string + Data [128]byte + } + + for _, c := range caches { + cacheName := b.Name() + cache := c.factory(b, cacheName) + for i := 0; i < size; i++ { + e := entry{ + ID: uuid.Must(uuid.NewV4()).String(), + } + err := cache.Put(e.ID, e) + require.NoError(b, err) + } + cache.Close() + + b.Run(c.name, func(b *testing.B) { + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + cache := c.factory(b, cacheName) + cache.Close() + } + }) + } + }) + } +} + +func BenchmarkGet(b *testing.B) { + type cache interface { + Put(key string, value interface{}) error + Get(key string, value interface{}) error + Close() error + } + + options := testOptions(b) + newPersistentCache := func(tb testing.TB, name string) cache { + cache, err := New(name, options) + require.NoError(tb, err) + return cache + } + + caches := []struct { + name string + factory func(t testing.TB, name string) cache + }{ + {name: "badger", factory: newPersistentCache}, + } + + for _, size := range benchmarkCacheSizes { + b.Run(fmt.Sprintf("%d objects", size), func(b *testing.B) { + for _, c := range caches { + type entry struct { + ID string + Data [128]byte + } + + cacheName := b.Name() + + objects := make([]entry, size) + cache := c.factory(b, cacheName) + for i := 0; i < size; i++ { + e := entry{ + ID: uuid.Must(uuid.NewV4()).String(), + } + objects[i] = e + err := cache.Put(e.ID, e) + require.NoError(b, err) + } + cache.Close() + + b.Run(c.name, func(b *testing.B) { + b.ReportAllocs() + + cache := c.factory(b, cacheName) + + var result entry + + b.ResetTimer() + for i := 0; i < b.N; i++ { + expected := objects[rand.Intn(size)] + cache.Get(expected.ID, &result) + if expected.ID != result.ID { + b.Fatalf("%s != %s", expected.ID, result.ID) + } + } + b.StopTimer() + cache.Close() + }) + } + }) + } +} + +func testOptions(t testing.TB) Options { + t.Helper() + + tempDir, err := ioutil.TempDir("", "beat-data-dir-") + require.NoError(t, err) + + t.Cleanup(func() { os.RemoveAll(tempDir) }) + + return Options{ + RootPath: filepath.Join(tempDir, cacheFile), + } +} + +func dirSize(tb testing.TB, path string) int64 { + var size int64 + + err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { + size += info.Size() + } + return nil + }) + require.NoError(tb, err) + + return size +} diff --git a/x-pack/libbeat/persistentcache/store.go b/x-pack/libbeat/persistentcache/store.go new file mode 100644 index 00000000000..589fc724e01 --- /dev/null +++ b/x-pack/libbeat/persistentcache/store.go @@ -0,0 +1,141 @@ +// 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 persistentcache + +import ( + "fmt" + "os" + "path/filepath" + "time" + + badger "github.com/dgraph-io/badger/v2" + + "github.com/elastic/beats/v7/libbeat/logp" +) + +// Store is a store for a persistent cache. It can be shared between consumers. +type Store struct { + logger *logp.Logger + name string + + gcQuit chan struct{} + db *badger.DB +} + +// newStore opens a store persisted on the specified directory, with the specified name. +// If succeeds, returned store must be closed. +func newStore(logger *logp.Logger, dir, name string) (*Store, error) { + dbPath := filepath.Join(dir, name) + err := os.MkdirAll(dbPath, 0750) + if err != nil { + return nil, fmt.Errorf("creating directory for cache store: %w", err) + } + + // Opinionated options for the use of badger as a store for metadata caches in Beats. + options := badger.DefaultOptions(dbPath) + options.Logger = badgerLogger{logger.Named("badger")} + // TODO: Disabling sync writes gives better performance, and data loss wouldn't + // be a problem for caches. But we are not properly closing processors yet, so let + // sync on writes by now. + // options.SyncWrites = false + + db, err := badger.Open(options) + if err != nil { + return nil, fmt.Errorf("opening database for cache store: %w", err) + } + + store := Store{ + db: db, + logger: logger, + name: name, + gcQuit: make(chan struct{}), + } + go store.runGC(gcPeriod) + return &store, nil +} + +// Close closes the store. +func (s *Store) Close() error { + s.stopGC() + err := s.db.Close() + if err != nil { + return fmt.Errorf("closing database of cache store: %w", err) + } + return nil +} + +// Set sets a value with a ttl in the store. If ttl is zero, it is ignored. +func (s *Store) Set(k, v []byte, ttl time.Duration) error { + entry := badger.Entry{ + Key: []byte(k), + Value: v, + } + if ttl > 0 { + entry.WithTTL(ttl) + } + err := s.db.Update(func(txn *badger.Txn) error { + return txn.SetEntry(&entry) + }) + if err != nil { + return fmt.Errorf("setting value in cache store: %w", err) + } + return err +} + +// Get gets a value from the store. +func (s *Store) Get(k []byte) ([]byte, error) { + var result []byte + err := s.db.View(func(txn *badger.Txn) error { + item, err := txn.Get(k) + if err != nil { + return err + } + result, err = item.ValueCopy(nil) + if err != nil { + return err + } + return nil + }) + if err != nil { + return nil, fmt.Errorf("getting value from cache store: %w", err) + } + return result, nil +} + +// runGC starts garbage collection in the store. +func (s *Store) runGC(period time.Duration) { + ticker := time.NewTicker(period) + defer ticker.Stop() + for { + select { + case <-ticker.C: + var err error + count := 0 + for err == nil { + err = s.db.RunValueLogGC(0.5) + count++ + } + s.logger.Debugf("Result of garbage collector after running %d times: %s", count, err) + case <-s.gcQuit: + return + } + } + +} + +// stopGC stops garbage collection in the store. +func (s *Store) stopGC() { + close(s.gcQuit) +} + +// badgerLogger is an adapter between a logp logger and the loggers expected by badger. +type badgerLogger struct { + *logp.Logger +} + +// Warningf logs a message at the warning level. +func (l badgerLogger) Warningf(format string, args ...interface{}) { + l.Warnf(format, args...) +} diff --git a/x-pack/libbeat/persistentcache/store_test.go b/x-pack/libbeat/persistentcache/store_test.go new file mode 100644 index 00000000000..efa51f24b7f --- /dev/null +++ b/x-pack/libbeat/persistentcache/store_test.go @@ -0,0 +1,43 @@ +// 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 persistentcache + +import ( + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/elastic/beats/v7/libbeat/logp" +) + +func TestStandaloneStore(t *testing.T) { + type valueType struct { + Something string + } + + var key = []byte("somekey") + var value = []byte("somevalue") + + tempDir, err := ioutil.TempDir("", "beat-data-dir-") + require.NoError(t, err) + t.Cleanup(func() { os.RemoveAll(tempDir) }) + + store, err := newStore(logp.NewLogger("test"), tempDir, "store-cache") + require.NoError(t, err) + + err = store.Set(key, value, 0) + assert.NoError(t, err) + + result, err := store.Get(key) + if assert.NoError(t, err) { + assert.Equal(t, value, result) + } + + err = store.Close() + assert.NoError(t, err) +} diff --git a/x-pack/libbeat/processors/add_cloudfoundry_metadata/add_cloudfoundry_metadata.go b/x-pack/libbeat/processors/add_cloudfoundry_metadata/add_cloudfoundry_metadata.go index d18a04ca979..c178ea04325 100644 --- a/x-pack/libbeat/processors/add_cloudfoundry_metadata/add_cloudfoundry_metadata.go +++ b/x-pack/libbeat/processors/add_cloudfoundry_metadata/add_cloudfoundry_metadata.go @@ -5,8 +5,6 @@ package add_cloudfoundry_metadata import ( - "time" - "github.com/pkg/errors" "github.com/elastic/beats/v7/libbeat/beat" @@ -40,14 +38,11 @@ func New(cfg *common.Config) (processors.Processor, error) { log := logp.NewLogger(selector) hub := cloudfoundry.NewHub(&config, "add_cloudfoundry_metadata", log) - client, err := hub.Client() + client, err := hub.ClientWithCache() if err != nil { - log.Debugf("%s: failed to created cloudfoundry client: %+v", processorName, err) + return nil, errors.Wrapf(err, "%s: creating cloudfoundry client", processorName) } - // Janitor run every 5 minutes to clean up the client cache. - client.StartJanitor(5 * time.Minute) - return &addCloudFoundryMetadata{ log: log, client: client, @@ -79,18 +74,31 @@ func (d *addCloudFoundryMetadata) Run(event *beat.Event) (*beat.Event, error) { "name": app.Name, }, "space": common.MapStr{ - "id": app.SpaceData.Meta.Guid, - "name": app.SpaceData.Entity.Name, + "id": app.SpaceGuid, + "name": app.SpaceName, }, "org": common.MapStr{ - "id": app.SpaceData.Entity.OrgData.Meta.Guid, - "name": app.SpaceData.Entity.OrgData.Entity.Name, + "id": app.OrgGuid, + "name": app.OrgName, }, }, }) return event, nil } +// String returns this processor name. func (d *addCloudFoundryMetadata) String() string { return processorName } + +// Close closes the underlying client and releases its resources. +func (d *addCloudFoundryMetadata) Close() error { + if d.client == nil { + return nil + } + err := d.client.Close() + if err != nil { + return errors.Wrap(err, "closing client") + } + return nil +} diff --git a/x-pack/libbeat/processors/add_cloudfoundry_metadata/add_cloudfoundry_metadata_test.go b/x-pack/libbeat/processors/add_cloudfoundry_metadata/add_cloudfoundry_metadata_test.go index 1aff4cb2df8..95a7073321e 100644 --- a/x-pack/libbeat/processors/add_cloudfoundry_metadata/add_cloudfoundry_metadata_test.go +++ b/x-pack/libbeat/processors/add_cloudfoundry_metadata/add_cloudfoundry_metadata_test.go @@ -6,7 +6,6 @@ package add_cloudfoundry_metadata import ( "testing" - "time" "github.com/cloudfoundry-community/go-cfclient" "github.com/gofrs/uuid" @@ -15,6 +14,7 @@ import ( "github.com/elastic/beats/v7/libbeat/beat" "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/logp" + "github.com/elastic/beats/v7/x-pack/libbeat/common/cloudfoundry" ) func TestNoClient(t *testing.T) { @@ -84,25 +84,13 @@ func TestCFAppNotFound(t *testing.T) { func TestCFAppUpdated(t *testing.T) { guid := mustCreateFakeGuid() - app := cfclient.App{ - Guid: guid, - Name: "My Fake App", - SpaceData: cfclient.SpaceResource{ - Meta: cfclient.Meta{ - Guid: mustCreateFakeGuid(), - }, - Entity: cfclient.Space{ - Name: "My Fake Space", - OrgData: cfclient.OrgResource{ - Meta: cfclient.Meta{ - Guid: mustCreateFakeGuid(), - }, - Entity: cfclient.Org{ - Name: "My Fake Org", - }, - }, - }, - }, + app := cloudfoundry.AppMeta{ + Guid: guid, + Name: "My Fake App", + SpaceGuid: mustCreateFakeGuid(), + SpaceName: "My Fake Space", + OrgGuid: mustCreateFakeGuid(), + OrgName: "My Fake Org", } p := addCloudFoundryMetadata{ log: logp.NewLogger("add_cloudfoundry_metadata"), @@ -126,12 +114,12 @@ func TestCFAppUpdated(t *testing.T) { "name": app.Name, }, "space": common.MapStr{ - "id": app.SpaceData.Meta.Guid, - "name": app.SpaceData.Entity.Name, + "id": app.SpaceGuid, + "name": app.SpaceName, }, "org": common.MapStr{ - "id": app.SpaceData.Entity.OrgData.Meta.Guid, - "name": app.SpaceData.Entity.OrgData.Entity.Name, + "id": app.OrgGuid, + "name": app.OrgName, }, }, }, @@ -142,20 +130,18 @@ func TestCFAppUpdated(t *testing.T) { } type fakeClient struct { - app cfclient.App + app cloudfoundry.AppMeta } -func (c *fakeClient) GetAppByGuid(guid string) (*cfclient.App, error) { +func (c *fakeClient) GetAppByGuid(guid string) (*cloudfoundry.AppMeta, error) { if c.app.Guid != guid { return nil, cfclient.CloudFoundryError{Code: 100004} } return &c.app, nil } -func (c *fakeClient) StartJanitor(_ time.Duration) { -} - -func (c *fakeClient) StopJanitor() { +func (c *fakeClient) Close() error { + return nil } func mustCreateFakeGuid() string { From 6c0a78617bbc9bfb1b0b5f0353adb797fb27701b Mon Sep 17 00:00:00 2001 From: StefanSa <6105075+StefanSa@users.noreply.github.com> Date: Tue, 6 Oct 2020 10:45:48 +0200 Subject: [PATCH 28/93] junipersrx-module initial release (#20017) * junipersrx-module initial release * stashing changes for later * Initial MVP release ready for review * updating a comment in pipeline.yml * updating filebeat.reference.yml * Small fix for docs * Fix parsing of juniper.srx.timestamp * Fix bad samples * Remove some fields to make the index-pattern smaller * Missing update * Fix var.tags and disable_host when forwarded * Add related fields * Add changelog entry * Remove unused file Co-authored-by: StefanSa Co-authored-by: P1llus Co-authored-by: Adrian Serrano Co-authored-by: Marc Guasch --- CHANGELOG.next.asciidoc | 1 + filebeat/docs/fields.asciidoc | 967 ++++++++ filebeat/docs/modules/juniper.asciidoc | 121 +- x-pack/filebeat/filebeat.reference.yml | 13 + .../filebeat/module/juniper/_meta/config.yml | 13 + .../module/juniper/_meta/docs.asciidoc | 121 +- x-pack/filebeat/module/juniper/fields.go | 2 +- .../test/generated.log-expected.json | 4 +- .../module/juniper/srx/_meta/fields.yml | 488 ++++ .../module/juniper/srx/config/srx.yml | 31 + .../module/juniper/srx/ingest/atp.yml | 363 +++ .../module/juniper/srx/ingest/flow.yml | 360 +++ .../module/juniper/srx/ingest/idp.yml | 287 +++ .../module/juniper/srx/ingest/ids.yml | 363 +++ .../module/juniper/srx/ingest/pipeline.yml | 275 +++ .../module/juniper/srx/ingest/secintel.yml | 349 +++ .../module/juniper/srx/ingest/utm.yml | 388 ++++ .../filebeat/module/juniper/srx/manifest.yml | 26 + .../filebeat/module/juniper/srx/test/atp.log | 4 + .../juniper/srx/test/atp.log-expected.json | 240 ++ .../filebeat/module/juniper/srx/test/flow.log | 25 + .../juniper/srx/test/flow.log-expected.json | 2013 +++++++++++++++++ .../filebeat/module/juniper/srx/test/idp.log | 7 + .../juniper/srx/test/idp.log-expected.json | 537 +++++ .../filebeat/module/juniper/srx/test/ids.log | 12 + .../juniper/srx/test/ids.log-expected.json | 699 ++++++ .../module/juniper/srx/test/secintel.log | 2 + .../srx/test/secintel.log-expected.json | 140 ++ .../filebeat/module/juniper/srx/test/utm.log | 12 + .../juniper/srx/test/utm.log-expected.json | 698 ++++++ .../filebeat/modules.d/juniper.yml.disabled | 13 + 31 files changed, 8563 insertions(+), 11 deletions(-) create mode 100644 x-pack/filebeat/module/juniper/srx/_meta/fields.yml create mode 100644 x-pack/filebeat/module/juniper/srx/config/srx.yml create mode 100644 x-pack/filebeat/module/juniper/srx/ingest/atp.yml create mode 100644 x-pack/filebeat/module/juniper/srx/ingest/flow.yml create mode 100644 x-pack/filebeat/module/juniper/srx/ingest/idp.yml create mode 100644 x-pack/filebeat/module/juniper/srx/ingest/ids.yml create mode 100644 x-pack/filebeat/module/juniper/srx/ingest/pipeline.yml create mode 100644 x-pack/filebeat/module/juniper/srx/ingest/secintel.yml create mode 100644 x-pack/filebeat/module/juniper/srx/ingest/utm.yml create mode 100644 x-pack/filebeat/module/juniper/srx/manifest.yml create mode 100644 x-pack/filebeat/module/juniper/srx/test/atp.log create mode 100644 x-pack/filebeat/module/juniper/srx/test/atp.log-expected.json create mode 100644 x-pack/filebeat/module/juniper/srx/test/flow.log create mode 100644 x-pack/filebeat/module/juniper/srx/test/flow.log-expected.json create mode 100644 x-pack/filebeat/module/juniper/srx/test/idp.log create mode 100644 x-pack/filebeat/module/juniper/srx/test/idp.log-expected.json create mode 100644 x-pack/filebeat/module/juniper/srx/test/ids.log create mode 100644 x-pack/filebeat/module/juniper/srx/test/ids.log-expected.json create mode 100644 x-pack/filebeat/module/juniper/srx/test/secintel.log create mode 100644 x-pack/filebeat/module/juniper/srx/test/secintel.log-expected.json create mode 100644 x-pack/filebeat/module/juniper/srx/test/utm.log create mode 100644 x-pack/filebeat/module/juniper/srx/test/utm.log-expected.json diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 5676634d637..07e7bc4be0d 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -607,6 +607,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Keep cursor state between httpjson input restarts {pull}20751[20751] - Convert aws s3 to v2 input {pull}20005[20005] - New Cisco Umbrella dataset {pull}21504[21504] +- New juniper.srx dataset for Juniper SRX logs. {pull}20017[20017] *Heartbeat* diff --git a/filebeat/docs/fields.asciidoc b/filebeat/docs/fields.asciidoc index b66c1163367..a2f19000095 100644 --- a/filebeat/docs/fields.asciidoc +++ b/filebeat/docs/fields.asciidoc @@ -88076,6 +88076,973 @@ type: keyword -- This key captures values or decorators used within a registry entry +type: keyword + +-- + +[float] +=== juniper.srx + +Module for parsing junipersrx syslog. + + + +*`juniper.srx.reason`*:: ++ +-- +reason + + +type: keyword + +-- + +*`juniper.srx.connection_tag`*:: ++ +-- +connection tag + + +type: keyword + +-- + +*`juniper.srx.service_name`*:: ++ +-- +service name + + +type: keyword + +-- + +*`juniper.srx.nat_connection_tag`*:: ++ +-- +nat connection tag + + +type: keyword + +-- + +*`juniper.srx.src_nat_rule_type`*:: ++ +-- +src nat rule type + + +type: keyword + +-- + +*`juniper.srx.src_nat_rule_name`*:: ++ +-- +src nat rule name + + +type: keyword + +-- + +*`juniper.srx.dst_nat_rule_type`*:: ++ +-- +dst nat rule type + + +type: keyword + +-- + +*`juniper.srx.dst_nat_rule_name`*:: ++ +-- +dst nat rule name + + +type: keyword + +-- + +*`juniper.srx.protocol_id`*:: ++ +-- +protocol id + + +type: keyword + +-- + +*`juniper.srx.policy_name`*:: ++ +-- +policy name + + +type: keyword + +-- + +*`juniper.srx.session_id_32`*:: ++ +-- +session id 32 + + +type: keyword + +-- + +*`juniper.srx.session_id`*:: ++ +-- +session id + + +type: keyword + +-- + +*`juniper.srx.outbound_packets`*:: ++ +-- +packets from client + + +type: integer + +-- + +*`juniper.srx.outbound_bytes`*:: ++ +-- +bytes from client + + +type: integer + +-- + +*`juniper.srx.inbound_packets`*:: ++ +-- +packets from server + + +type: integer + +-- + +*`juniper.srx.inbound_bytes`*:: ++ +-- +bytes from server + + +type: integer + +-- + +*`juniper.srx.elapsed_time`*:: ++ +-- +elapsed time + + +type: date + +-- + +*`juniper.srx.application`*:: ++ +-- +application + + +type: keyword + +-- + +*`juniper.srx.nested_application`*:: ++ +-- +nested application + + +type: keyword + +-- + +*`juniper.srx.username`*:: ++ +-- +username + + +type: keyword + +-- + +*`juniper.srx.roles`*:: ++ +-- +roles + + +type: keyword + +-- + +*`juniper.srx.encrypted`*:: ++ +-- +encrypted + + +type: keyword + +-- + +*`juniper.srx.application_category`*:: ++ +-- +application category + + +type: keyword + +-- + +*`juniper.srx.application_sub_category`*:: ++ +-- +application sub category + + +type: keyword + +-- + +*`juniper.srx.application_characteristics`*:: ++ +-- +application characteristics + + +type: keyword + +-- + +*`juniper.srx.secure_web_proxy_session_type`*:: ++ +-- +secure web proxy session type + + +type: keyword + +-- + +*`juniper.srx.peer_session_id`*:: ++ +-- +peer session id + + +type: keyword + +-- + +*`juniper.srx.peer_source_address`*:: ++ +-- +peer source address + + +type: ip + +-- + +*`juniper.srx.peer_source_port`*:: ++ +-- +peer source port + + +type: integer + +-- + +*`juniper.srx.peer_destination_address`*:: ++ +-- +peer destination address + + +type: ip + +-- + +*`juniper.srx.peer_destination_port`*:: ++ +-- +peer destination port + + +type: integer + +-- + +*`juniper.srx.hostname`*:: ++ +-- +hostname + + +type: keyword + +-- + +*`juniper.srx.src_vrf_grp`*:: ++ +-- +src_vrf_grp + + +type: keyword + +-- + +*`juniper.srx.dst_vrf_grp`*:: ++ +-- +dst_vrf_grp + + +type: keyword + +-- + +*`juniper.srx.icmp_type`*:: ++ +-- +icmp type + + +type: integer + +-- + +*`juniper.srx.process`*:: ++ +-- +process that generated the message + + +type: keyword + +-- + +*`juniper.srx.apbr_rule_type`*:: ++ +-- +apbr rule type + + +type: keyword + +-- + +*`juniper.srx.dscp_value`*:: ++ +-- +apbr rule type + + +type: integer + +-- + +*`juniper.srx.logical_system_name`*:: ++ +-- +logical system name + + +type: keyword + +-- + +*`juniper.srx.profile_name`*:: ++ +-- +profile name + + +type: keyword + +-- + +*`juniper.srx.routing_instance`*:: ++ +-- +routing instance + + +type: keyword + +-- + +*`juniper.srx.rule_name`*:: ++ +-- +rule name + + +type: keyword + +-- + +*`juniper.srx.uplink_tx_bytes`*:: ++ +-- +uplink tx bytes + + +type: integer + +-- + +*`juniper.srx.uplink_rx_bytes`*:: ++ +-- +uplink rx bytes + + +type: integer + +-- + +*`juniper.srx.obj`*:: ++ +-- +url path + + +type: keyword + +-- + +*`juniper.srx.url`*:: ++ +-- +url domain + + +type: keyword + +-- + +*`juniper.srx.profile`*:: ++ +-- +filter profile + + +type: keyword + +-- + +*`juniper.srx.category`*:: ++ +-- +filter category + + +type: keyword + +-- + +*`juniper.srx.filename`*:: ++ +-- +filename + + +type: keyword + +-- + +*`juniper.srx.temporary_filename`*:: ++ +-- +temporary_filename + + +type: keyword + +-- + +*`juniper.srx.name`*:: ++ +-- +name + + +type: keyword + +-- + +*`juniper.srx.error_message`*:: ++ +-- +error_message + + +type: keyword + +-- + +*`juniper.srx.error_code`*:: ++ +-- +error_code + + +type: keyword + +-- + +*`juniper.srx.action`*:: ++ +-- +action + + +type: keyword + +-- + +*`juniper.srx.protocol`*:: ++ +-- +protocol + + +type: keyword + +-- + +*`juniper.srx.protocol_name`*:: ++ +-- +protocol name + + +type: keyword + +-- + +*`juniper.srx.type`*:: ++ +-- +type + + +type: keyword + +-- + +*`juniper.srx.repeat_count`*:: ++ +-- +repeat count + + +type: integer + +-- + +*`juniper.srx.alert`*:: ++ +-- +repeat alert + + +type: keyword + +-- + +*`juniper.srx.message_type`*:: ++ +-- +message type + + +type: keyword + +-- + +*`juniper.srx.threat_severity`*:: ++ +-- +threat severity + + +type: keyword + +-- + +*`juniper.srx.application_name`*:: ++ +-- +application name + + +type: keyword + +-- + +*`juniper.srx.attack_name`*:: ++ +-- +attack name + + +type: keyword + +-- + +*`juniper.srx.index`*:: ++ +-- +index + + +type: keyword + +-- + +*`juniper.srx.message`*:: ++ +-- +mesagge + + +type: keyword + +-- + +*`juniper.srx.epoch_time`*:: ++ +-- +epoch time + + +type: date + +-- + +*`juniper.srx.packet_log_id`*:: ++ +-- +packet log id + + +type: integer + +-- + +*`juniper.srx.export_id`*:: ++ +-- +packet log id + + +type: integer + +-- + +*`juniper.srx.ddos_application_name`*:: ++ +-- +ddos application name + + +type: keyword + +-- + +*`juniper.srx.connection_hit_rate`*:: ++ +-- +connection hit rate + + +type: integer + +-- + +*`juniper.srx.time_scope`*:: ++ +-- +time scope + + +type: keyword + +-- + +*`juniper.srx.context_hit_rate`*:: ++ +-- +context hit rate + + +type: integer + +-- + +*`juniper.srx.context_value_hit_rate`*:: ++ +-- +context value hit rate + + +type: integer + +-- + +*`juniper.srx.time_count`*:: ++ +-- +time count + + +type: integer + +-- + +*`juniper.srx.time_period`*:: ++ +-- +time period + + +type: integer + +-- + +*`juniper.srx.context_value`*:: ++ +-- +context value + + +type: keyword + +-- + +*`juniper.srx.context_name`*:: ++ +-- +context name + + +type: keyword + +-- + +*`juniper.srx.ruleebase_name`*:: ++ +-- +ruleebase name + + +type: keyword + +-- + +*`juniper.srx.verdict_source`*:: ++ +-- +verdict source + + +type: keyword + +-- + +*`juniper.srx.verdict_number`*:: ++ +-- +verdict number + + +type: integer + +-- + +*`juniper.srx.file_category`*:: ++ +-- +file category + + +type: keyword + +-- + +*`juniper.srx.sample_sha256`*:: ++ +-- +sample sha256 + + +type: keyword + +-- + +*`juniper.srx.malware_info`*:: ++ +-- +malware info + + +type: keyword + +-- + +*`juniper.srx.client_ip`*:: ++ +-- +client ip + + +type: ip + +-- + +*`juniper.srx.tenant_id`*:: ++ +-- +tenant id + + +type: keyword + +-- + +*`juniper.srx.timestamp`*:: ++ +-- +timestamp + + +type: date + +-- + +*`juniper.srx.th`*:: ++ +-- +th + + +type: keyword + +-- + +*`juniper.srx.status`*:: ++ +-- +status + + +type: keyword + +-- + +*`juniper.srx.state`*:: ++ +-- +state + + +type: keyword + +-- + +*`juniper.srx.file_hash_lookup`*:: ++ +-- +file hash lookup + + +type: keyword + +-- + +*`juniper.srx.file_name`*:: ++ +-- +file name + + +type: keyword + +-- + +*`juniper.srx.action_detail`*:: ++ +-- +action detail + + +type: keyword + +-- + +*`juniper.srx.sub_category`*:: ++ +-- +sub category + + +type: keyword + +-- + +*`juniper.srx.feed_name`*:: ++ +-- +feed name + + +type: keyword + +-- + +*`juniper.srx.occur_count`*:: ++ +-- +occur count + + +type: integer + +-- + +*`juniper.srx.tag`*:: ++ +-- +system log message tag, which uniquely identifies the message. + + type: keyword -- diff --git a/filebeat/docs/modules/juniper.asciidoc b/filebeat/docs/modules/juniper.asciidoc index 047e847bc5a..a2d2a0100d3 100644 --- a/filebeat/docs/modules/juniper.asciidoc +++ b/filebeat/docs/modules/juniper.asciidoc @@ -10,18 +10,131 @@ This file is generated! See scripts/docs_collector.py == Juniper module -experimental[] +This is a module for ingesting data from the different Juniper Products. Currently supports these filesets: -This is a module for receiving Juniper JUNOS logs over Syslog or a file. +- `srx` fileset: Supports Juniper SRX logs +- `junos` fileset: Supports Juniper JUNOS logs +- `netscreen` fileset: Supports Juniper Netscreen logs include::../include/gs-link.asciidoc[] include::../include/configuring-intro.asciidoc[] -:fileset_ex: junos - include::../include/config-option-intro.asciidoc[] +:fileset_ex: srx +beta[] + +[float] +==== `srx` fileset settings + +The Juniper-SRX module only supports syslog messages in the format "structured-data + brief" https://www.juniper.net/documentation/en_US/junos/topics/reference/configuration-statement/structured-data-edit-system.html[JunOS Documentation structured-data] + +To configure a remote syslog destination, please reference the https://kb.juniper.net/InfoCenter/index?page=content&id=kb16502[SRX Getting Started - Configure System Logging]. + +The following processes and tags are supported: + +[options="header"] +|============================================================== +| JunOS processes | JunOS tags | +| RT_FLOW | RT_FLOW_SESSION_CREATE | +| | RT_FLOW_SESSION_CLOSE | +| | RT_FLOW_SESSION_DENY | +| | APPTRACK_SESSION_CREATE | +| | APPTRACK_SESSION_CLOSE | +| | APPTRACK_SESSION_VOL_UPDATE | +| RT_IDS | RT_SCREEN_TCP | +| | RT_SCREEN_UDP | +| | RT_SCREEN_ICMP | +| | RT_SCREEN_IP | +| | RT_SCREEN_TCP_DST_IP | +| | RT_SCREEN_TCP_SRC_IP | +| RT_UTM | WEBFILTER_URL_PERMITTED | +| | WEBFILTER_URL_BLOCKED | +| | AV_VIRUS_DETECTED_MT | +| | CONTENT_FILTERING_BLOCKED_MT | +| | ANTISPAM_SPAM_DETECTED_MT | +| RT_IDP | IDP_ATTACK_LOG_EVENT | +| | IDP_APPDDOS_APP_STATE_EVENT | +| RT_AAMW | SRX_AAMW_ACTION_LOG | +| | AAMW_MALWARE_EVENT_LOG | +| | AAMW_HOST_INFECTED_EVENT_LOG | +| | AAMW_ACTION_LOG | +| RT_SECINTEL | SECINTEL_ACTION_LOG | +|============================================================== + +The syslog format choosen should be `Default`. + +[float] +=== Compatibility + +This module has been tested against JunOS version 19.x and 20.x. +Versions above this are expected to work but have not been tested. + +[source,yaml] +---- +- module: sophosxg + firewall: + enabled: true + var.input: udp + var.syslog_host: 0.0.0.0 + var.syslog_port: 9006 +---- + +include::../include/var-paths.asciidoc[] + +*`var.input`*:: + +The input to use, can be either the value `tcp`, `udp` or `file`. + +*`var.syslog_host`*:: + +The interface to listen to all syslog traffic. Defaults to localhost. +Set to 0.0.0.0 to bind to all available interfaces. + +*`var.syslog_port`*:: + +The port to listen for syslog traffic. Defaults to 9006. + + +[float] +==== Juniper SRX ECS fields + +This is a list of JunOS fields that are mapped to ECS. + +[options="header"] +|============================================================== +| Juniper SRX Fields | ECS Fields | +| application-risk | event.risk_score | +| bytes-from-client | source.bytes | +| bytes-from-server | destination.bytes | +| destination-interface-name | observer.egress.interface.name | +| destination-zone-name | observer.egress.zone | +| destination-address | destination.ip | +| destination-port | destination.port | +| dst_domainname | url.domain | +| elapsed-time | event.duration | +| filename | file.name | +| nat-destination-address | destination.nat.ip | +| nat-destination-port | destination.nat.port | +| nat-source-address | source.nat.ip | +| nat-source-port | source.nat.port | +| message | message | +| obj | url.path | +| packets-from-client | source.packets | +| packets-from-server | destination.packets | +| policy-name | rule.name | +| protocol | network.transport | +| source-address | source.ip | +| source-interface-name | observer.ingress.interface.name| +| source-port | source.port | +| source-zone-name | observer.ingress.zone | +| url | url.domain | +|============================================================== + + +:fileset_ex: junos + [float] ==== `junos` fileset settings diff --git a/x-pack/filebeat/filebeat.reference.yml b/x-pack/filebeat/filebeat.reference.yml index 9797291bdf4..cc994b45cac 100644 --- a/x-pack/filebeat/filebeat.reference.yml +++ b/x-pack/filebeat/filebeat.reference.yml @@ -1050,6 +1050,19 @@ filebeat.modules: # "+02:00" for GMT+02:00 # var.tz_offset: local + srx: + enabled: true + + # Set which input to use between tcp, udp (default) or file. + #var.input: udp + + # The interface to listen to syslog traffic. Defaults to + # localhost. Set to 0.0.0.0 to bind to all available interfaces. + #var.syslog_host: localhost + + # The port to listen for syslog traffic. Defaults to 9006. + #var.syslog_port: 9006 + #-------------------------------- Kafka Module -------------------------------- - module: kafka # All logs diff --git a/x-pack/filebeat/module/juniper/_meta/config.yml b/x-pack/filebeat/module/juniper/_meta/config.yml index be40af66202..7f992656788 100644 --- a/x-pack/filebeat/module/juniper/_meta/config.yml +++ b/x-pack/filebeat/module/juniper/_meta/config.yml @@ -36,3 +36,16 @@ # "local" (default) for system timezone. # "+02:00" for GMT+02:00 # var.tz_offset: local + + srx: + enabled: true + + # Set which input to use between tcp, udp (default) or file. + #var.input: udp + + # The interface to listen to syslog traffic. Defaults to + # localhost. Set to 0.0.0.0 to bind to all available interfaces. + #var.syslog_host: localhost + + # The port to listen for syslog traffic. Defaults to 9006. + #var.syslog_port: 9006 diff --git a/x-pack/filebeat/module/juniper/_meta/docs.asciidoc b/x-pack/filebeat/module/juniper/_meta/docs.asciidoc index c59b7ac4a95..3e145ea81c9 100644 --- a/x-pack/filebeat/module/juniper/_meta/docs.asciidoc +++ b/x-pack/filebeat/module/juniper/_meta/docs.asciidoc @@ -5,18 +5,131 @@ == Juniper module -experimental[] +This is a module for ingesting data from the different Juniper Products. Currently supports these filesets: -This is a module for receiving Juniper JUNOS logs over Syslog or a file. +- `srx` fileset: Supports Juniper SRX logs +- `junos` fileset: Supports Juniper JUNOS logs +- `netscreen` fileset: Supports Juniper Netscreen logs include::../include/gs-link.asciidoc[] include::../include/configuring-intro.asciidoc[] -:fileset_ex: junos - include::../include/config-option-intro.asciidoc[] +:fileset_ex: srx +beta[] + +[float] +==== `srx` fileset settings + +The Juniper-SRX module only supports syslog messages in the format "structured-data + brief" https://www.juniper.net/documentation/en_US/junos/topics/reference/configuration-statement/structured-data-edit-system.html[JunOS Documentation structured-data] + +To configure a remote syslog destination, please reference the https://kb.juniper.net/InfoCenter/index?page=content&id=kb16502[SRX Getting Started - Configure System Logging]. + +The following processes and tags are supported: + +[options="header"] +|============================================================== +| JunOS processes | JunOS tags | +| RT_FLOW | RT_FLOW_SESSION_CREATE | +| | RT_FLOW_SESSION_CLOSE | +| | RT_FLOW_SESSION_DENY | +| | APPTRACK_SESSION_CREATE | +| | APPTRACK_SESSION_CLOSE | +| | APPTRACK_SESSION_VOL_UPDATE | +| RT_IDS | RT_SCREEN_TCP | +| | RT_SCREEN_UDP | +| | RT_SCREEN_ICMP | +| | RT_SCREEN_IP | +| | RT_SCREEN_TCP_DST_IP | +| | RT_SCREEN_TCP_SRC_IP | +| RT_UTM | WEBFILTER_URL_PERMITTED | +| | WEBFILTER_URL_BLOCKED | +| | AV_VIRUS_DETECTED_MT | +| | CONTENT_FILTERING_BLOCKED_MT | +| | ANTISPAM_SPAM_DETECTED_MT | +| RT_IDP | IDP_ATTACK_LOG_EVENT | +| | IDP_APPDDOS_APP_STATE_EVENT | +| RT_AAMW | SRX_AAMW_ACTION_LOG | +| | AAMW_MALWARE_EVENT_LOG | +| | AAMW_HOST_INFECTED_EVENT_LOG | +| | AAMW_ACTION_LOG | +| RT_SECINTEL | SECINTEL_ACTION_LOG | +|============================================================== + +The syslog format choosen should be `Default`. + +[float] +=== Compatibility + +This module has been tested against JunOS version 19.x and 20.x. +Versions above this are expected to work but have not been tested. + +[source,yaml] +---- +- module: sophosxg + firewall: + enabled: true + var.input: udp + var.syslog_host: 0.0.0.0 + var.syslog_port: 9006 +---- + +include::../include/var-paths.asciidoc[] + +*`var.input`*:: + +The input to use, can be either the value `tcp`, `udp` or `file`. + +*`var.syslog_host`*:: + +The interface to listen to all syslog traffic. Defaults to localhost. +Set to 0.0.0.0 to bind to all available interfaces. + +*`var.syslog_port`*:: + +The port to listen for syslog traffic. Defaults to 9006. + + +[float] +==== Juniper SRX ECS fields + +This is a list of JunOS fields that are mapped to ECS. + +[options="header"] +|============================================================== +| Juniper SRX Fields | ECS Fields | +| application-risk | event.risk_score | +| bytes-from-client | source.bytes | +| bytes-from-server | destination.bytes | +| destination-interface-name | observer.egress.interface.name | +| destination-zone-name | observer.egress.zone | +| destination-address | destination.ip | +| destination-port | destination.port | +| dst_domainname | url.domain | +| elapsed-time | event.duration | +| filename | file.name | +| nat-destination-address | destination.nat.ip | +| nat-destination-port | destination.nat.port | +| nat-source-address | source.nat.ip | +| nat-source-port | source.nat.port | +| message | message | +| obj | url.path | +| packets-from-client | source.packets | +| packets-from-server | destination.packets | +| policy-name | rule.name | +| protocol | network.transport | +| source-address | source.ip | +| source-interface-name | observer.ingress.interface.name| +| source-port | source.port | +| source-zone-name | observer.ingress.zone | +| url | url.domain | +|============================================================== + + +:fileset_ex: junos + [float] ==== `junos` fileset settings diff --git a/x-pack/filebeat/module/juniper/fields.go b/x-pack/filebeat/module/juniper/fields.go index 6122a564654..e22907d0244 100644 --- a/x-pack/filebeat/module/juniper/fields.go +++ b/x-pack/filebeat/module/juniper/fields.go @@ -19,5 +19,5 @@ func init() { // AssetJuniper returns asset data. // This is the base64 encoded gzipped contents of module/juniper. func AssetJuniper() string { - return "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" + return "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" } diff --git a/x-pack/filebeat/module/juniper/netscreen/test/generated.log-expected.json b/x-pack/filebeat/module/juniper/netscreen/test/generated.log-expected.json index fb4fca25df2..da17c3a5f76 100644 --- a/x-pack/filebeat/module/juniper/netscreen/test/generated.log-expected.json +++ b/x-pack/filebeat/module/juniper/netscreen/test/generated.log-expected.json @@ -2399,8 +2399,8 @@ "observer.type": "Firewall", "observer.vendor": "Juniper", "related.ip": [ - "10.119.181.171", - "10.166.144.66" + "10.166.144.66", + "10.119.181.171" ], "rsa.internal.messageid": "00625", "rsa.misc.hardware_id": "dol", diff --git a/x-pack/filebeat/module/juniper/srx/_meta/fields.yml b/x-pack/filebeat/module/juniper/srx/_meta/fields.yml new file mode 100644 index 00000000000..55ded3a11e6 --- /dev/null +++ b/x-pack/filebeat/module/juniper/srx/_meta/fields.yml @@ -0,0 +1,488 @@ +- name: juniper.srx + type: group + release: beta + default_field: false + overwrite: true + description: > + Module for parsing junipersrx syslog. + fields: + - name: reason + type: keyword + description: > + reason + + - name: connection_tag + type: keyword + description: > + connection tag + + - name: service_name + type: keyword + description: > + service name + + - name: nat_connection_tag + type: keyword + description: > + nat connection tag + + - name: src_nat_rule_type + type: keyword + description: > + src nat rule type + + - name: src_nat_rule_name + type: keyword + description: > + src nat rule name + + - name: dst_nat_rule_type + type: keyword + description: > + dst nat rule type + + - name: dst_nat_rule_name + type: keyword + description: > + dst nat rule name + + - name: protocol_id + type: keyword + description: > + protocol id + + - name: policy_name + type: keyword + description: > + policy name + + - name: session_id_32 + type: keyword + description: > + session id 32 + + - name: session_id + type: keyword + description: > + session id + + - name: outbound_packets + type: integer + description: > + packets from client + + - name: outbound_bytes + type: integer + description: > + bytes from client + + - name: inbound_packets + type: integer + description: > + packets from server + + - name: inbound_bytes + type: integer + description: > + bytes from server + + - name: elapsed_time + type: date + description: > + elapsed time + + - name: application + type: keyword + description: > + application + + - name: nested_application + type: keyword + description: > + nested application + + - name: username + type: keyword + description: > + username + + - name: roles + type: keyword + description: > + roles + + - name: encrypted + type: keyword + description: > + encrypted + + - name: application_category + type: keyword + description: > + application category + + - name: application_sub_category + type: keyword + description: > + application sub category + + - name: application_characteristics + type: keyword + description: > + application characteristics + + - name: secure_web_proxy_session_type + type: keyword + description: > + secure web proxy session type + + - name: peer_session_id + type: keyword + description: > + peer session id + + - name: peer_source_address + type: ip + description: > + peer source address + + - name: peer_source_port + type: integer + description: > + peer source port + + - name: peer_destination_address + type: ip + description: > + peer destination address + + - name: peer_destination_port + type: integer + description: > + peer destination port + + - name: hostname + type: keyword + description: > + hostname + + - name: src_vrf_grp + type: keyword + description: > + src_vrf_grp + + - name: dst_vrf_grp + type: keyword + description: > + dst_vrf_grp + + - name: icmp_type + type: integer + description: > + icmp type + + - name: process + type: keyword + description: > + process that generated the message + + - name: apbr_rule_type + type: keyword + description: > + apbr rule type + + - name: dscp_value + type: integer + description: > + apbr rule type + + - name: logical_system_name + type: keyword + description: > + logical system name + + - name: profile_name + type: keyword + description: > + profile name + + - name: routing_instance + type: keyword + description: > + routing instance + + - name: rule_name + type: keyword + description: > + rule name + + - name: uplink_tx_bytes + type: integer + description: > + uplink tx bytes + + - name: uplink_rx_bytes + type: integer + description: > + uplink rx bytes + + - name: obj + type: keyword + description: > + url path + + - name: url + type: keyword + description: > + url domain + + - name: profile + type: keyword + description: > + filter profile + + - name: category + type: keyword + description: > + filter category + + - name: filename + type: keyword + description: > + filename + + - name: temporary_filename + type: keyword + description: > + temporary_filename + + - name: name + type: keyword + description: > + name + + - name: error_message + type: keyword + description: > + error_message + + - name: error_code + type: keyword + description: > + error_code + + - name: action + type: keyword + description: > + action + + - name: protocol + type: keyword + description: > + protocol + + - name: protocol_name + type: keyword + description: > + protocol name + + - name: type + type: keyword + description: > + type + + - name: repeat_count + type: integer + description: > + repeat count + + - name: alert + type: keyword + description: > + repeat alert + + - name: message_type + type: keyword + description: > + message type + + - name: threat_severity + type: keyword + description: > + threat severity + + - name: application_name + type: keyword + description: > + application name + + - name: attack_name + type: keyword + description: > + attack name + + - name: index + type: keyword + description: > + index + + - name: message + type: keyword + description: > + mesagge + + - name: epoch_time + type: date + description: > + epoch time + + - name: packet_log_id + type: integer + description: > + packet log id + + - name: export_id + type: integer + description: > + packet log id + + - name: ddos_application_name + type: keyword + description: > + ddos application name + + - name: connection_hit_rate + type: integer + description: > + connection hit rate + + - name: time_scope + type: keyword + description: > + time scope + + - name: context_hit_rate + type: integer + description: > + context hit rate + + - name: context_value_hit_rate + type: integer + description: > + context value hit rate + + - name: time_count + type: integer + description: > + time count + + - name: time_period + type: integer + description: > + time period + + - name: context_value + type: keyword + description: > + context value + + - name: context_name + type: keyword + description: > + context name + + - name: ruleebase_name + type: keyword + description: > + ruleebase name + + - name: verdict_source + type: keyword + description: > + verdict source + + - name: verdict_number + type: integer + description: > + verdict number + + - name: file_category + type: keyword + description: > + file category + + - name: sample_sha256 + type: keyword + description: > + sample sha256 + + - name: malware_info + type: keyword + description: > + malware info + + - name: client_ip + type: ip + description: > + client ip + + - name: tenant_id + type: keyword + description: > + tenant id + + - name: timestamp + type: date + description: > + timestamp + + - name: th + type: keyword + description: > + th + + - name: status + type: keyword + description: > + status + + - name: state + type: keyword + description: > + state + + - name: file_hash_lookup + type: keyword + description: > + file hash lookup + + - name: file_name + type: keyword + description: > + file name + + - name: action_detail + type: keyword + description: > + action detail + + - name: sub_category + type: keyword + description: > + sub category + + - name: feed_name + type: keyword + description: > + feed name + + - name: occur_count + type: integer + description: > + occur count + + - name: tag + type: keyword + description: > + system log message tag, which uniquely identifies the message. + diff --git a/x-pack/filebeat/module/juniper/srx/config/srx.yml b/x-pack/filebeat/module/juniper/srx/config/srx.yml new file mode 100644 index 00000000000..6af16945317 --- /dev/null +++ b/x-pack/filebeat/module/juniper/srx/config/srx.yml @@ -0,0 +1,31 @@ +{{ if eq .input "tcp" }} + +type: tcp +host: "{{.syslog_host}}:{{.syslog_port}}" + +{{ else if eq .input "udp" }} + +type: udp +host: "{{.syslog_host}}:{{.syslog_port}}" + +{{ else if eq .input "file" }} + +type: log +paths: +{{ range $i, $path := .paths }} + - {{$path}} +{{ end }} + +exclude_files: [".gz$"] + +{{ end }} + +tags: {{.tags | tojson}} +publisher_pipeline.disable_host: {{ inList .tags "forwarded" }} + +processors: + - add_locale: ~ + - add_fields: + target: '' + fields: + ecs.version: 1.5.0 diff --git a/x-pack/filebeat/module/juniper/srx/ingest/atp.yml b/x-pack/filebeat/module/juniper/srx/ingest/atp.yml new file mode 100644 index 00000000000..b93e8da9f98 --- /dev/null +++ b/x-pack/filebeat/module/juniper/srx/ingest/atp.yml @@ -0,0 +1,363 @@ +description: Pipeline for parsing junipersrx firewall logs (atp pipeline) +processors: +####################### +## ECS Event Mapping ## +####################### +- set: + field: event.kind + value: event +- set: + field: event.outcome + value: success + if: "ctx.juniper?.srx?.tag != null" +- append: + field: event.category + value: network +- set: + field: event.kind + value: alert + if: '["SRX_AAMW_ACTION_LOG", "AAMW_MALWARE_EVENT_LOG", "AAMW_HOST_INFECTED_EVENT_LOG", "AAMW_ACTION_LOG"].contains(ctx.juniper?.srx?.tag) && ctx.juniper?.srx?.action != "PERMIT"' +- append: + field: event.category + value: malware + if: '["SRX_AAMW_ACTION_LOG", "AAMW_MALWARE_EVENT_LOG", "AAMW_HOST_INFECTED_EVENT_LOG", "AAMW_ACTION_LOG"].contains(ctx.juniper?.srx?.tag) && ctx.juniper?.srx?.action != "PERMIT"' +- append: + field: event.type + value: + - info + - denied + - connection + if: "ctx.juniper?.srx?.action == 'BLOCK' || ctx.juniper?.srx?.tag == 'AAMW_MALWARE_EVENT_LOG'" +- append: + field: event.type + value: + - allowed + - connection + if: "ctx.juniper?.srx?.action != 'BLOCK' && ctx.juniper?.srx?.tag != 'AAMW_MALWARE_EVENT_LOG'" +- set: + field: event.action + value: malware_detected + if: "ctx.juniper?.srx?.action == 'BLOCK' || ctx.juniper?.srx?.tag == 'AAMW_MALWARE_EVENT_LOG'" + + +#################################### +## ECS Server/Destination Mapping ## +#################################### +- rename: + field: juniper.srx.destination_address + target_field: destination.ip + ignore_missing: true + if: "ctx.juniper?.srx?.destination_address != null" +- set: + field: server.ip + value: '{{destination.ip}}' + if: "ctx.destination?.ip != null" +- rename: + field: juniper.srx.nat_destination_address + target_field: destination.nat.ip + ignore_missing: true + if: "ctx.juniper?.srx?.nat_destination_address != null" +- convert: + field: juniper.srx.destination_port + target_field: destination.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.destination_port != null" +- set: + field: server.port + value: '{{destination.port}}' + if: "ctx.destination?.port != null" +- convert: + field: server.port + target_field: server.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.port != null" +- convert: + field: juniper.srx.nat_destination_port + target_field: destination.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.nat_destination_port != null" +- set: + field: server.nat.port + value: '{{destination.nat.port}}' + if: "ctx.destination?.nat?.port != null" +- convert: + field: server.nat.port + target_field: server.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.nat?.port != null" +- convert: + field: juniper.srx.bytes_from_server + target_field: destination.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.bytes_from_server != null" +- set: + field: server.bytes + value: '{{destination.bytes}}' + if: "ctx.destination?.bytes != null" +- convert: + field: server.bytes + target_field: server.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.bytes != null" +- convert: + field: juniper.srx.packets_from_server + target_field: destination.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.packets_from_server != null" +- set: + field: server.packets + value: '{{destination.packets}}' + if: "ctx.destination?.packets != null" +- convert: + field: server.packets + target_field: server.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.packets != null" + +############################### +## ECS Client/Source Mapping ## +############################### +- rename: + field: juniper.srx.source_address + target_field: source.ip + ignore_missing: true + if: "ctx.juniper?.srx?.source_address != null" +- set: + field: client.ip + value: '{{source.ip}}' + if: "ctx.source?.ip != null" +- rename: + field: juniper.srx.nat_source_address + target_field: source.nat.ip + ignore_missing: true + if: "ctx.juniper?.srx?.nat_source_address != null" +- rename: + field: juniper.srx.sourceip + target_field: source.ip + ignore_missing: true + if: "ctx.juniper?.srx?.sourceip != null" +- convert: + field: juniper.srx.source_port + target_field: source.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.source_port != null" +- set: + field: client.port + value: '{{source.port}}' + if: "ctx.source?.port != null" +- convert: + field: client.port + target_field: client.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.port != null" +- convert: + field: juniper.srx.nat_source_port + target_field: source.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.nat_source_port != null" +- set: + field: client.nat.port + value: '{{source.nat.port}}' + if: "ctx.source?.nat?.port != null" +- convert: + field: client.nat.port + target_field: client.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.nat?.port != null" +- convert: + field: juniper.srx.bytes_from_client + target_field: source.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.bytes_from_client != null" +- set: + field: client.bytes + value: '{{source.bytes}}' + if: "ctx.source?.bytes != null" +- convert: + field: client.bytes + target_field: client.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.bytes != null" +- convert: + field: juniper.srx.packets_from_client + target_field: source.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.packets_from_client != null" +- set: + field: client.packets + value: '{{source.packets}}' + if: "ctx.source?.packets != null" +- convert: + field: client.packets + target_field: client.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.packets != null" +- rename: + field: juniper.srx.username + target_field: source.user.name + ignore_missing: true + if: "ctx.juniper?.srx?.username != null" +- rename: + field: juniper.srx.hostname + target_field: source.domain + ignore_missing: true + if: "ctx.juniper?.srx?.hostname != null" +- rename: + field: juniper.srx.client_ip + target_field: source.ip + ignore_missing: true + if: "ctx.juniper?.srx?.client_ip != null" + +###################### +## ECS URL Mapping ## +###################### +- rename: + field: juniper.srx.http_host + target_field: url.domain + ignore_missing: true + if: "ctx.juniper?.srx?.http_host != null" + +############################# +## ECS Network/Geo Mapping ## +############################# +- rename: + field: juniper.srx.protocol_id + target_field: network.iana_number + ignore_missing: true + if: "ctx.juniper?.srx?.protocol_id != null" +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true + if: "ctx.source?.geo == null" +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true + if: "ctx.destination?.geo == null" +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + field: source.nat.ip + target_field: source.geo + ignore_missing: true + if: "ctx.source?.geo == null" +- geoip: + field: destination.nat.ip + target_field: destination.geo + ignore_missing: true + if: "ctx.destination?.geo == null" +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.nat.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true + if: "ctx.source?.as == null" +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.nat.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true + if: "ctx.destination?.as == null" +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +############### +## Timestamp ## +############### +- date: + if: 'ctx.juniper.srx?.timestamp != null' + field: juniper.srx.timestamp + target_field: juniper.srx.timestamp + formats: + - 'EEE MMM dd HH:mm:ss yyyy' + - 'EEE MMM d HH:mm:ss yyyy' + on_failure: + - remove: + field: + - juniper.srx.timestamp + +############# +## Cleanup ## +############# +- remove: + field: + - juniper.srx.destination_port + - juniper.srx.nat_destination_port + - juniper.srx.bytes_from_client + - juniper.srx.packets_from_client + - juniper.srx.source_port + - juniper.srx.nat_source_port + - juniper.srx.bytes_from_server + - juniper.srx.packets_from_server + ignore_missing: true + +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/juniper/srx/ingest/flow.yml b/x-pack/filebeat/module/juniper/srx/ingest/flow.yml new file mode 100644 index 00000000000..1a488a57bd8 --- /dev/null +++ b/x-pack/filebeat/module/juniper/srx/ingest/flow.yml @@ -0,0 +1,360 @@ +description: Pipeline for parsing junipersrx firewall logs (flow pipeline) +processors: +####################### +## ECS Event Mapping ## +####################### +- set: + field: event.kind + value: event +- set: + field: event.outcome + value: success + if: "ctx.juniper?.srx?.tag != null" +- append: + field: event.category + value: network +- rename: + field: juniper.srx.application_risk + target_field: event.risk_score + ignore_missing: true + if: "ctx.juniper?.srx?.application_risk != null" +- append: + field: event.type + value: + - start + - allowed + - connection + if: "ctx.juniper?.srx?.tag.endsWith('CREATE') || ctx.juniper?.srx?.tag.endsWith('UPDATE') || ctx.juniper?.srx?.tag.endsWith('CREATE_LS') || ctx.juniper?.srx?.tag.endsWith('UPDATE_LS')" +- append: + field: event.type + value: + - end + - allowed + - connection + if: "ctx.juniper?.srx?.tag.endsWith('CLOSE') || ctx.juniper?.srx?.tag.endsWith('CLOSE_LS')" +- append: + field: event.type + value: + - denied + - connection + if: "ctx.juniper?.srx?.tag.endsWith('DENY') || ctx.juniper?.srx?.tag.endsWith('DENY_LS')" +- set: + field: event.action + value: flow_started + if: "ctx.juniper?.srx?.tag.endsWith('CREATE') || ctx.juniper?.srx?.tag.endsWith('UPDATE') || ctx.juniper?.srx?.tag.endsWith('CREATE_LS') || ctx.juniper?.srx?.tag.endsWith('UPDATE_LS')" +- set: + field: event.action + value: flow_close + if: "ctx.juniper?.srx?.tag.endsWith('CLOSE') || ctx.juniper?.srx?.tag.endsWith('CLOSE_LS')" +- set: + field: event.action + value: flow_deny + if: "ctx.juniper?.srx?.tag.endsWith('DENY') || ctx.juniper?.srx?.tag.endsWith('DENY_LS')" + +#################################### +## ECS Server/Destination Mapping ## +#################################### +- rename: + field: juniper.srx.destination_address + target_field: destination.ip + ignore_missing: true + if: "ctx.juniper?.srx?.destination_address != null" +- set: + field: server.ip + value: '{{destination.ip}}' + if: "ctx.destination?.ip != null" +- rename: + field: juniper.srx.nat_destination_address + target_field: destination.nat.ip + ignore_missing: true + if: "ctx.juniper?.srx?.nat_destination_address != null" +- convert: + field: juniper.srx.destination_port + target_field: destination.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.destination_port != null" +- set: + field: server.port + value: '{{destination.port}}' + if: "ctx?.destination?.port != null" +- convert: + field: server.port + target_field: server.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.port != null" +- convert: + field: juniper.srx.nat_destination_port + target_field: destination.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.nat_destination_port != null" +- set: + field: server.nat.port + value: '{{destination.nat.port}}' + if: "ctx.destination?.nat?.port != null" +- convert: + field: server.nat.port + target_field: server.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.nat?.port != null" +- convert: + field: juniper.srx.bytes_from_server + target_field: destination.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.bytes_from_server != null" +- set: + field: server.bytes + value: '{{destination.bytes}}' + if: "ctx.destination?.bytes != null" +- convert: + field: server.bytes + target_field: server.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.bytes != null" +- convert: + field: juniper.srx.packets_from_server + target_field: destination.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.packets_from_server != null" +- set: + field: server.packets + value: '{{destination.packets}}' + if: "ctx.destination?.packets != null" +- convert: + field: server.packets + target_field: server.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.packets != null" + +############################### +## ECS Client/Source Mapping ## +############################### +- rename: + field: juniper.srx.source_address + target_field: source.ip + ignore_missing: true + if: "ctx.juniper?.srx?.source_address != null" +- set: + field: client.ip + value: '{{source.ip}}' + if: "ctx.source?.ip != null" +- rename: + field: juniper.srx.nat_source_address + target_field: source.nat.ip + ignore_missing: true + if: "ctx.juniper?.srx?.nat_source_address != null" +- rename: + field: juniper.srx.sourceip + target_field: source.ip + ignore_missing: true + if: "ctx.juniper?.srx?.sourceip != null" +- convert: + field: juniper.srx.source_port + target_field: source.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.source_port != null" +- set: + field: client.port + value: '{{source.port}}' + if: "ctx.source?.port != null" +- convert: + field: client.port + target_field: client.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.port != null" +- convert: + field: juniper.srx.nat_source_port + target_field: source.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.nat_source_port != null" +- set: + field: client.nat.port + value: '{{source.nat.port}}' + if: "ctx.source?.nat?.port != null" +- convert: + field: client.nat.port + target_field: client.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.nat?.port != null" +- convert: + field: juniper.srx.bytes_from_client + target_field: source.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.bytes_from_client != null" +- set: + field: client.bytes + value: '{{source.bytes}}' + if: "ctx.source?.bytes != null" +- convert: + field: client.bytes + target_field: client.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.bytes != null" +- convert: + field: juniper.srx.packets_from_client + target_field: source.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.packets_from_client != null" +- set: + field: client.packets + value: '{{source.packets}}' + if: "ctx.source?.packets != null" +- convert: + field: client.packets + target_field: client.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.packets != null" +- rename: + field: juniper.srx.username + target_field: source.user.name + ignore_missing: true + if: "ctx.juniper?.srx?.username != null" + +###################### +## ECS Rule Mapping ## +###################### +- rename: + field: juniper.srx.policy_name + target_field: rule.name + ignore_missing: true + if: "ctx.juniper?.srx?.policy_name != null" + +############################# +## ECS Network/Geo Mapping ## +############################# +- rename: + field: juniper.srx.protocol_id + target_field: network.iana_number + ignore_missing: true + if: "ctx.juniper?.srx?.protocol_id != null" +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true + if: "ctx.source?.geo == null" +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true + if: "ctx.destination?.geo == null" +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + field: source.nat.ip + target_field: source.geo + ignore_missing: true + if: "ctx.source?.geo == null" +- geoip: + field: destination.nat.ip + target_field: destination.geo + ignore_missing: true + if: "ctx.destination?.geo == null" +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.nat.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true + if: "ctx.source?.as == null" +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.nat.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true + if: "ctx.destination?.as == null" +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +- script: + lang: painless + source: "ctx.network.bytes = ctx.source.bytes + ctx.destination.bytes" + if: "ctx?.source?.bytes != null && ctx?.destination?.bytes != null" + ignore_failure: true +- script: + lang: painless + source: "ctx.network.packets = ctx.client.packets + ctx.server.packets" + if: "ctx?.client?.packets != null && ctx?.server?.packets != null" + ignore_failure: true + +############# +## Cleanup ## +############# +- remove: + field: + - juniper.srx.destination_port + - juniper.srx.nat_destination_port + - juniper.srx.bytes_from_client + - juniper.srx.packets_from_client + - juniper.srx.source_port + - juniper.srx.nat_source_port + - juniper.srx.bytes_from_server + - juniper.srx.packets_from_server + ignore_missing: true + +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/juniper/srx/ingest/idp.yml b/x-pack/filebeat/module/juniper/srx/ingest/idp.yml new file mode 100644 index 00000000000..808185410d7 --- /dev/null +++ b/x-pack/filebeat/module/juniper/srx/ingest/idp.yml @@ -0,0 +1,287 @@ +description: Pipeline for parsing junipersrx firewall logs (idp pipeline) +processors: +####################### +## ECS Event Mapping ## +####################### +- set: + field: event.kind + value: event +- set: + field: event.outcome + value: success + if: "ctx.juniper?.srx?.tag != null" +- append: + field: event.category + value: network +- set: + field: event.kind + value: alert + if: '["IDP_ATTACK_LOG_EVENT", "IDP_APPDDOS_APP_STATE_EVENT", "IDP_APPDDOS_APP_ATTACK_EVENT", "IDP_ATTACK_LOG_EVENT_LS", "IDP_APPDDOS_APP_STATE_EVENT_LS", "IDP_APPDDOS_APP_ATTACK_EVENT_LS"].contains(ctx.juniper?.srx?.tag)' +- append: + field: event.category + value: intrusion_detection + if: '["IDP_ATTACK_LOG_EVENT", "IDP_APPDDOS_APP_STATE_EVENT", "IDP_APPDDOS_APP_ATTACK_EVENT", "IDP_ATTACK_LOG_EVENT_LS", "IDP_APPDDOS_APP_STATE_EVENT_LS", "IDP_APPDDOS_APP_ATTACK_EVENT_LS"].contains(ctx.juniper?.srx?.tag)' +- append: + field: event.type + value: + - info + - denied + - connection + if: '["IDP_ATTACK_LOG_EVENT", "IDP_APPDDOS_APP_STATE_EVENT", "IDP_APPDDOS_APP_ATTACK_EVENT", "IDP_ATTACK_LOG_EVENT_LS", "IDP_APPDDOS_APP_STATE_EVENT_LS", "IDP_APPDDOS_APP_ATTACK_EVENT_LS"].contains(ctx.juniper?.srx?.tag)' +- append: + field: event.type + value: + - allowed + - connection + if: '!["IDP_ATTACK_LOG_EVENT", "IDP_APPDDOS_APP_STATE_EVENT", "IDP_APPDDOS_APP_ATTACK_EVENT", "IDP_ATTACK_LOG_EVENT_LS", "IDP_APPDDOS_APP_STATE_EVENT_LS", "IDP_APPDDOS_APP_ATTACK_EVENT_LS"].contains(ctx.juniper?.srx?.tag)' +- set: + field: event.action + value: application_ddos + if: '["IDP_APPDDOS_APP_STATE_EVENT", "IDP_APPDDOS_APP_ATTACK_EVENT", "IDP_APPDDOS_APP_STATE_EVENT_LS", "IDP_APPDDOS_APP_ATTACK_EVENT_LS"].contains(ctx.juniper?.srx?.tag)' +- set: + field: event.action + value: security_threat + if: '["IDP_ATTACK_LOG_EVENT", "IDP_ATTACK_LOG_EVENT_LS"].contains(ctx.juniper?.srx?.tag)' + + +#################################### +## ECS Server/Destination Mapping ## +#################################### +- rename: + field: juniper.srx.destination_address + target_field: destination.ip + ignore_missing: true + if: "ctx.juniper?.srx?.destination_address != null" +- set: + field: server.ip + value: '{{destination.ip}}' + if: "ctx.destination?.ip != null" +- rename: + field: juniper.srx.nat_destination_address + target_field: destination.nat.ip + ignore_missing: true + if: "ctx.juniper?.srx?.nat_destination_address != null" +- convert: + field: juniper.srx.destination_port + target_field: destination.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.destination_port != null" +- set: + field: server.port + value: '{{destination.port}}' + if: "ctx.destination?.port != null" +- convert: + field: server.port + target_field: server.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.port != null" +- convert: + field: juniper.srx.nat_destination_port + target_field: destination.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx['nat_destination_port'] != null" +- set: + field: server.nat.port + value: '{{destination.nat.port}}' + if: "ctx.destination?.nat?.port != null" +- convert: + field: server.nat.port + target_field: server.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.nat?.port != null" +- convert: + field: juniper.srx.inbound_bytes + target_field: destination.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.inbound_bytes != null" +- set: + field: server.bytes + value: '{{destination.bytes}}' + if: "ctx.destination?.bytes != null" +- convert: + field: server.bytes + target_field: server.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.bytes != null" +- convert: + field: juniper.srx.inbound_packets + target_field: destination.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.inbound_packets !=null" +- set: + field: server.packets + value: '{{destination.packets}}' + if: "ctx.destination?.packets != null" +- convert: + field: server.packets + target_field: server.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.packets != null" + +############################### +## ECS Client/Source Mapping ## +############################### +- rename: + field: juniper.srx.source_address + target_field: source.ip + ignore_missing: true + if: "ctx.juniper?.srx?.source_address != null" +- set: + field: client.ip + value: '{{source.ip}}' + if: "ctx.source?.ip != null" +- rename: + field: juniper.srx.nat_source_address + target_field: source.nat.ip + ignore_missing: true + if: "ctx.juniper?.srx?.nat_source_address != null" +- rename: + field: juniper.srx.sourceip + target_field: source.ip + ignore_missing: true + if: "ctx.juniper?.srx?.sourceip != null" +- convert: + field: juniper.srx.source_port + target_field: source.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.source_port != null" +- set: + field: client.port + value: '{{source.port}}' + if: "ctx.source?.port != null" +- convert: + field: client.port + target_field: client.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.port != null" +- convert: + field: juniper.srx.nat_source_port + target_field: source.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.nat_source_port != null" +- set: + field: client.nat.port + value: '{{source.nat.port}}' + if: "ctx.source?.nat?.port != null" +- convert: + field: client.nat.port + target_field: client.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.nat?.port != null" +- convert: + field: juniper.srx.outbound_bytes + target_field: source.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.outbound_bytes != null" +- set: + field: client.bytes + value: '{{source.bytes}}' + if: "ctx.source?.bytes != null" +- convert: + field: client.bytes + target_field: client.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.bytes != null" +- convert: + field: juniper.srx.outbound_packets + target_field: source.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.outbound_packets != null" +- set: + field: client.packets + value: '{{source.packets}}' + if: "ctx.source?.packets != null" +- convert: + field: client.packets + target_field: client.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.packets != null" +- rename: + field: juniper.srx.username + target_field: source.user.name + ignore_missing: true + if: "ctx.juniper?.srx?.username != null" + +###################### +## ECS Rule Mapping ## +###################### +- rename: + field: juniper.srx.rulebase_name + target_field: rule.name + ignore_missing: true + if: "ctx.juniper?.srx?.rulebase_name != null" +- rename: + field: juniper.srx.rule_name + target_field: rule.id + ignore_missing: true + if: "ctx.juniper?.srx?.rule_name != null" + +######################### +## ECS Network Mapping ## +######################### +- rename: + field: juniper.srx.protocol_name + target_field: network.protocol + ignore_missing: true + if: "ctx.juniper?.srx?.protocol_name != null" + +######################### +## ECS message Mapping ## +######################### +- rename: + field: juniper.srx.message + target_field: message + ignore_missing: true + if: "ctx.juniper?.srx?.message != null" + +############# +## Cleanup ## +############# +- remove: + field: + - juniper.srx.destination_port + - juniper.srx.nat_destination_port + - juniper.srx.outbound_bytes + - juniper.srx.outbound_packets + - juniper.srx.source_port + - juniper.srx.nat_source_port + - juniper.srx.inbound_bytes + - juniper.srx.inbound_packets + ignore_missing: true + +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/juniper/srx/ingest/ids.yml b/x-pack/filebeat/module/juniper/srx/ingest/ids.yml new file mode 100644 index 00000000000..039fdd64ccb --- /dev/null +++ b/x-pack/filebeat/module/juniper/srx/ingest/ids.yml @@ -0,0 +1,363 @@ +description: Pipeline for parsing junipersrx firewall logs (ids pipeline) +processors: +####################### +## ECS Event Mapping ## +####################### +- set: + field: event.kind + value: event +- set: + field: event.outcome + value: success + if: "ctx.juniper?.srx?.tag != null" +- append: + field: event.category + value: network +- set: + field: event.kind + value: alert + if: '["RT_SCREEN_TCP", "RT_SCREEN_UDP", "RT_SCREEN_ICMP", "RT_SCREEN_IP", "RT_SCREEN_TCP_DST_IP", "RT_SCREEN_TCP_SRC_IP", "RT_SCREEN_TCP_LS", "RT_SCREEN_UDP_LS", "RT_SCREEN_ICMP_LS", "RT_SCREEN_IP_LS", "RT_SCREEN_TCP_DST_IP_LS", "RT_SCREEN_TCP_SRC_IP_LS"].contains(ctx.juniper?.srx?.tag)' +- append: + field: event.category + value: intrusion_detection + if: '["RT_SCREEN_TCP", "RT_SCREEN_UDP", "RT_SCREEN_ICMP", "RT_SCREEN_IP", "RT_SCREEN_TCP_DST_IP", "RT_SCREEN_TCP_SRC_IP", "RT_SCREEN_TCP_LS", "RT_SCREEN_UDP_LS", "RT_SCREEN_ICMP_LS", "RT_SCREEN_IP_LS", "RT_SCREEN_TCP_DST_IP_LS", "RT_SCREEN_TCP_SRC_IP_LS"].contains(ctx.juniper?.srx?.tag)' +- append: + field: event.type + value: + - info + - denied + - connection + if: '["RT_SCREEN_TCP", "RT_SCREEN_UDP", "RT_SCREEN_ICMP", "RT_SCREEN_IP", "RT_SCREEN_TCP_DST_IP", "RT_SCREEN_TCP_SRC_IP", "RT_SCREEN_TCP_LS", "RT_SCREEN_UDP_LS", "RT_SCREEN_ICMP_LS", "RT_SCREEN_IP_LS", "RT_SCREEN_TCP_DST_IP_LS", "RT_SCREEN_TCP_SRC_IP_LS"].contains(ctx.juniper?.srx?.tag)' +- append: + field: event.type + value: + - allowed + - connection + if: '!["RT_SCREEN_TCP", "RT_SCREEN_UDP", "RT_SCREEN_ICMP", "RT_SCREEN_IP", "RT_SCREEN_TCP_DST_IP", "RT_SCREEN_TCP_SRC_IP", "RT_SCREEN_TCP_LS", "RT_SCREEN_UDP_LS", "RT_SCREEN_ICMP_LS", "RT_SCREEN_IP_LS", "RT_SCREEN_TCP_DST_IP_LS", "RT_SCREEN_TCP_SRC_IP_LS"].contains(ctx.juniper?.srx?.tag)' +- set: + field: event.action + value: flood_detected + if: '["ICMP flood!", "UDP flood!", "SYN flood!", "SYN flood Src-IP based!", "SYN flood Dst-IP based!"].contains(ctx.juniper?.srx?.attack_name)' +- set: + field: event.action + value: scan_detected + if: "ctx.juniper?.srx?.attack_name == 'TCP port scan!'" +- set: + field: event.action + value: sweep_detected + if: '["TCP sweep!", "IP sweep!", "UDP sweep!", "Address sweep!"].contains(ctx.juniper?.srx?.attack_name)' +- set: + field: event.action + value: fragment_detected + if: '["ICMP fragment!", "SYN fragment!"].contains(ctx.juniper?.srx?.attack_name)' +- set: + field: event.action + value: spoofing_detected + if: "ctx.juniper?.srx?.attack_name == 'IP spoofing!'" +- set: + field: event.action + value: session_limit_detected + if: '["Src IP session limit!", "Dst IP session limit!"].contains(ctx.juniper?.srx?.attack_name)' +- set: + field: event.action + value: attack_detected + if: '["Land attack!", "WinNuke attack!"].contains(ctx.juniper?.srx?.attack_name)' +- set: + field: event.action + value: illegal_tcp_flag_detected + if: '["No TCP flag!", "SYN and FIN bits!", "FIN but no ACK bit!"].contains(ctx.juniper?.srx?.attack_name)' +- set: + field: event.action + value: tunneling_screen + if: "ctx.juniper?.srx?.attack_name.startsWith('Tunnel')" + + +#################################### +## ECS Server/Destination Mapping ## +#################################### +- rename: + field: juniper.srx.destination_address + target_field: destination.ip + ignore_missing: true + if: "ctx.juniper?.srx?.destination_address != null" +- set: + field: server.ip + value: '{{destination.ip}}' + if: "ctx.destination?.ip != null" +- rename: + field: juniper.srx.nat_destination_address + target_field: destination.nat.ip + ignore_missing: true + if: "ctx.juniper?.srx?.nat_destination_address != null" +- convert: + field: juniper.srx.destination_port + target_field: destination.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.destination_port != null" +- set: + field: server.port + value: '{{destination.port}}' + if: "ctx.destination?.port != null" +- convert: + field: server.port + target_field: server.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.port != null" +- convert: + field: juniper.srx.nat_destination_port + target_field: destination.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.nat_destination_port != null" +- set: + field: server.nat.port + value: '{{destination.nat.port}}' + if: "ctx.destination?.nat?.port != null" +- convert: + field: server.nat.port + target_field: server.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.nat?.port != null" +- convert: + field: juniper.srx.bytes_from_server + target_field: destination.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.bytes_from_server != null" +- set: + field: server.bytes + value: '{{destination.bytes}}' + if: "ctx.destination?.bytes != null" +- convert: + field: server.bytes + target_field: server.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.bytes != null" +- convert: + field: juniper.srx.packets_from_server + target_field: destination.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.packets_from_server !=null" +- set: + field: server.packets + value: '{{destination.packets}}' + if: "ctx.destination?.packets != null" +- convert: + field: server.packets + target_field: server.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.packets != null" + +############################### +## ECS Client/Source Mapping ## +############################### +- rename: + field: juniper.srx.source_address + target_field: source.ip + ignore_missing: true + if: "ctx.juniper?.srx?.source_address != null" +- set: + field: client.ip + value: '{{source.ip}}' + if: "ctx.source?.ip != null" +- rename: + field: juniper.srx.nat_source_address + target_field: source.nat.ip + ignore_missing: true + if: "ctx.juniper?.srx?.nat_source_address != null" +- rename: + field: juniper.srx.sourceip + target_field: source.ip + ignore_missing: true + if: "ctx.juniper?.srx?.sourceip != null" +- convert: + field: juniper.srx.source_port + target_field: source.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.source_port != null" +- set: + field: client.port + value: '{{source.port}}' + if: "ctx.source?.port != null" +- convert: + field: client.port + target_field: client.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.port != null" +- convert: + field: juniper.srx.nat_source_port + target_field: source.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.nat_source_port != null" +- set: + field: client.nat.port + value: '{{source.nat.port}}' + if: "ctx.source?.nat?.port != null" +- convert: + field: client.nat.port + target_field: client.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.nat?.port != null" +- convert: + field: juniper.srx.bytes_from_client + target_field: source.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.bytes_from_client != null" +- set: + field: client.bytes + value: '{{source.bytes}}' + if: "ctx.source?.bytes != null" +- convert: + field: client.bytes + target_field: client.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.bytes != null" +- convert: + field: juniper.srx.packets_from_client + target_field: source.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.packets_from_client != null" +- set: + field: client.packets + value: '{{source.packets}}' + if: "ctx.source?.packets != null" +- convert: + field: client.packets + target_field: client.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.packets != null" +- rename: + field: juniper.srx.username + target_field: source.user.name + ignore_missing: true + if: "ctx.juniper?.srx?.username != null" + +############################# +## ECS Network/Geo Mapping ## +############################# +- rename: + field: juniper.srx.protocol_id + target_field: network.iana_number + ignore_missing: true + if: "ctx.juniper?.srx?.protocol_id != null" +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true + if: "ctx.source?.geo == null" +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true + if: "ctx.destination?.geo == null" +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + field: source.nat.ip + target_field: source.geo + ignore_missing: true + if: "ctx.source?.geo == null" +- geoip: + field: destination.nat.ip + target_field: destination.geo + ignore_missing: true + if: "ctx.destination?.geo == null" +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.nat.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true + if: "ctx.source?.as == null" +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.nat.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true + if: "ctx.destination?.as == null" +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true + + +############# +## Cleanup ## +############# +- remove: + field: + - juniper.srx.destination_port + - juniper.srx.nat_destination_port + - juniper.srx.bytes_from_client + - juniper.srx.packets_from_client + - juniper.srx.source_port + - juniper.srx.nat_source_port + - juniper.srx.bytes_from_server + - juniper.srx.packets_from_server + ignore_missing: true + +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/juniper/srx/ingest/pipeline.yml b/x-pack/filebeat/module/juniper/srx/ingest/pipeline.yml new file mode 100644 index 00000000000..5bc4d45e82e --- /dev/null +++ b/x-pack/filebeat/module/juniper/srx/ingest/pipeline.yml @@ -0,0 +1,275 @@ +# This module only supports syslog messages in the format "structured-data + brief" +# https://www.juniper.net/documentation/en_US/junos/topics/reference/configuration-statement/structured-data-edit-system.html +description: Pipeline for parsing junipersrx firewall logs +processors: +- grok: + field: message + patterns: + - '^<%{POSINT:syslog_pri}>(\d{1,3}\s)?(?:%{TIMESTAMP_ISO8601:_temp_.raw_date})\s%{SYSLOGHOST:syslog_hostname}\s%{PROG:syslog_program}\s(?:%{POSINT:syslog_pid}|-)?\s%{WORD:log_type}\s\[.+?\s%{GREEDYDATA:log.original}\]$' + +# split Juniper-SRX fields +- kv: + field: log.original + field_split: " (?=[a-z0-9\\_\\-]+=)" + value_split: "=" + prefix: "juniper.srx." + ignore_missing: true + ignore_failure: false + trim_value: "\"" + +# Converts all kebab-case key names to snake_case +- script: + lang: painless + source: >- + ctx.juniper.srx = ctx?.juniper?.srx.entrySet().stream().collect(Collectors.toMap(e -> e.getKey().replace('-', '_'), e -> e.getValue())); + +# +# Parse the date +# +- date: + if: "ctx.event.timezone == null" + field: _temp_.raw_date + target_field: "@timestamp" + formats: + - yyyy-MM-dd HH:mm:ss + - yyyy-MM-dd HH:mm:ss z + - yyyy-MM-dd HH:mm:ss Z + - ISO8601 +- date: + if: "ctx.event.timezone != null" + timezone: "{{ event.timezone }}" + field: _temp_.raw_date + target_field: "@timestamp" + formats: + - yyyy-MM-dd HH:mm:ss + - yyyy-MM-dd HH:mm:ss z + - yyyy-MM-dd HH:mm:ss Z + - ISO8601 + +- set: + field: event.ingested + value: '{{_ingest.timestamp}}' + +# Can possibly be omitted if there is a solution for the equal signs and the calculation of the start time. +# -> juniper.srx.elapsed_time +- rename: + field: juniper.srx.elapsed_time + target_field: juniper.srx.duration + if: "ctx.juniper?.srx?.elapsed_time != null" + +# Sets starts, end and duration when start and duration is known +- script: + lang: painless + if: ctx?.juniper?.srx?.duration != null + source: >- + ctx.event.duration = Integer.parseInt(ctx.juniper.srx.duration) * 1000000000L; + ctx.event.start = ctx['@timestamp']; + ZonedDateTime start = ZonedDateTime.parse(ctx.event.start); + ctx.event.end = start.plus(ctx.event.duration, ChronoUnit.NANOS); + +# Removes all empty fields +- script: + lang: painless + params: + values: + - "None" + - "UNKNOWN" + - "N/A" + - "-" + source: >- + ctx?.juniper?.srx.entrySet().removeIf(entry -> params.values.contains(entry.getValue())); + +####################### +## ECS Event Mapping ## +####################### +- set: + field: event.module + value: juniper +- set: + field: event.dataset + value: juniper.srx +- set: + field: event.severity + value: '{{syslog_pri}}' +- rename: + field: log.original + target_field: event.original + ignore_missing: true + +##################### +## ECS Log Mapping ## +##################### +# https://www.juniper.net/documentation/en_US/junos/topics/reference/general/syslog-interpreting-msg-generated-structured-data-format.html#fac_sev_codes +- set: + field: "log.level" + if: '["0", "8", "16", "24", "32", "40", "48", "56", "64", "72", "80", "88", "96", "104", "112", "128", "136", "144", "152", "160", "168", "176", "184"].contains(ctx.syslog_pri)' + value: emergency +- set: + field: "log.level" + if: '["1", "9", "17", "25", "33", "41", "49", "57", "65", "73", "81", "89", "97", "105", "113", "129", "137", "145", "153", "161", "169", "177", "185"].contains(ctx.syslog_pri)' + value: alert +- set: + field: "log.level" + if: '["2", "10", "18", "26", "34", "42", "50", "58", "66", "74", "82", "90", "98", "106", "114", "130", "138", "146", "154", "162", "170", "178", "186"].contains(ctx.syslog_pri)' + value: critical +- set: + field: "log.level" + if: '["3", "11", "19", "27", "35", "43", "51", "59", "67", "75", "83", "91", "99", "107", "115", "131", "139", "147", "155", "163", "171", "179", "187"].contains(ctx.syslog_pri)' + value: error +- set: + field: "log.level" + if: '["4", "12", "20", "28", "36", "44", "52", "60", "68", "76", "84", "92", "100", "108", "116", "132", "140", "148", "156", "164", "172", "180", "188"].contains(ctx.syslog_pri)' + value: warning +- set: + field: "log.level" + if: '["5", "13", "21", "29", "37", "45", "53", "61", "69", "77", "85", "93", "101", "109", "117", "133", "141", "149", "157", "165", "173", "181", "189"].contains(ctx.syslog_pri)' + value: notification +- set: + field: "log.level" + if: '["6", "14", "22", "30", "38", "46", "54", "62", "70", "78", "86", "94", "102", "110", "118", "134", "142", "150", "158", "166", "174", "182", "190"].contains(ctx.syslog_pri)' + value: informational +- set: + field: "log.level" + if: '["7", "15", "23", "31", "39", "47", "55", "63", "71", "79", "87", "95", "103", "111", "119", "135", "143", "151", "159", "167", "175", "183", "191"].contains(ctx.syslog_pri)' + value: debug + +########################## +## ECS Observer Mapping ## +########################## +- set: + field: observer.vendor + value: Juniper +- set: + field: observer.product + value: SRX +- set: + field: observer.type + value: firewall +- rename: + field: syslog_hostname + target_field: observer.name + ignore_missing: true +- rename: + field: juniper.srx.packet_incoming_interface + target_field: observer.ingress.interface.name + ignore_missing: true +- rename: + field: juniper.srx.destination_interface_name + target_field: observer.egress.interface.name + ignore_missing: true +- rename: + field: juniper.srx.source_interface_name + target_field: observer.ingress.interface.name + ignore_missing: true +- rename: + field: juniper.srx.interface_name + target_field: observer.ingress.interface.name + ignore_missing: true +- rename: + field: juniper.srx.source_zone_name + target_field: observer.ingress.zone + ignore_missing: true +- rename: + field: juniper.srx.source_zone + target_field: observer.ingress.zone + ignore_missing: true +- rename: + field: juniper.srx.destination_zone_name + target_field: observer.egress.zone + ignore_missing: true +- rename: + field: juniper.srx.destination_zone + target_field: observer.egress.zone + ignore_missing: true +- rename: + field: syslog_program + target_field: juniper.srx.process + ignore_missing: true +- rename: + field: log_type + target_field: juniper.srx.tag + ignore_missing: true + + +############# +## Cleanup ## +############# +- remove: + field: + - message + - _temp_ + - _temp + - juniper.srx.duration + - juniper.srx.dir_disp + - juniper.srx.srczone + - juniper.srx.dstzone + - juniper.srx.duration + - syslog_pri + ignore_missing: true + +################################ +## Product Specific Pipelines ## +################################ +- pipeline: + name: '{< IngestPipeline "flow" >}' + if: "ctx.juniper?.srx?.process == 'RT_FLOW'" +- pipeline: + name: '{< IngestPipeline "utm" >}' + if: "ctx.juniper?.srx?.process == 'RT_UTM'" +- pipeline: + name: '{< IngestPipeline "idp" >}' + if: "ctx.juniper?.srx?.process == 'RT_IDP'" +- pipeline: + name: '{< IngestPipeline "ids" >}' + if: "ctx.juniper?.srx?.process == 'RT_IDS'" +- pipeline: + name: '{< IngestPipeline "atp" >}' + if: "ctx.juniper?.srx?.process == 'RT_AAMW'" +- pipeline: + name: '{< IngestPipeline "secintel" >}' + if: "ctx.juniper?.srx?.process == 'RT_SECINTEL'" + +######################### +## ECS Related Mapping ## +######################### +- append: + if: 'ctx.source?.ip != null' + field: related.ip + value: '{{source.ip}}' + ignore_failure: true +- append: + if: 'ctx.destination?.ip != null' + field: related.ip + value: '{{destination.ip}}' + ignore_failure: true +- append: + if: 'ctx.source?.nat?.ip != null' + field: related.ip + value: '{{source.nat.ip}}' + ignore_failure: true +- append: + if: 'ctx?.destination?.nat?.ip != null' + field: related.ip + value: '{{destination.nat.ip}}' + ignore_failure: true + +- append: + if: 'ctx.url?.domain != null' + field: related.hosts + value: '{{url.domain}}' + ignore_failure: true +- append: + if: 'ctx.source?.domain != null' + field: related.hosts + value: '{{source.domain}}' + ignore_failure: true +- append: + if: 'ctx.destination?.domain != null' + field: related.hosts + value: '{{destination.domain}}' + ignore_failure: true + +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/juniper/srx/ingest/secintel.yml b/x-pack/filebeat/module/juniper/srx/ingest/secintel.yml new file mode 100644 index 00000000000..f2abb2bcf9c --- /dev/null +++ b/x-pack/filebeat/module/juniper/srx/ingest/secintel.yml @@ -0,0 +1,349 @@ +description: Pipeline for parsing junipersrx firewall logs (secintel pipeline) +processors: +####################### +## ECS Event Mapping ## +####################### +- set: + field: event.kind + value: event +- set: + field: event.outcome + value: success + if: "ctx.juniper?.srx?.tag != null" +- append: + field: event.category + value: network +- set: + field: event.kind + value: alert + if: 'ctx.juniper?.srx?.tag == "SECINTEL_ACTION_LOG" && ctx.juniper?.srx?.action != "PERMIT"' +- append: + field: event.category + value: malware + if: 'ctx.juniper?.srx?.tag == "SECINTEL_ACTION_LOG" && ctx.juniper?.srx?.action != "PERMIT"' +- append: + field: event.type + value: + - info + - denied + - connection + if: "ctx.juniper?.srx?.action == 'BLOCK'" +- append: + field: event.type + value: + - allowed + - connection + if: "ctx.juniper?.srx?.action != 'BLOCK'" +- set: + field: event.action + value: malware_detected + if: "ctx.juniper?.srx?.action == 'BLOCK'" + + +#################################### +## ECS Server/Destination Mapping ## +#################################### +- rename: + field: juniper.srx.destination_address + target_field: destination.ip + ignore_missing: true + if: "ctx.juniper?.srx?.destination_address != null" +- set: + field: server.ip + value: '{{destination.ip}}' + if: "ctx.destination?.ip != null" +- rename: + field: juniper.srx.nat_destination_address + target_field: destination.nat.ip + ignore_missing: true + if: "ctx.juniper?.srx?.nat_destination_address != null" +- convert: + field: juniper.srx.destination_port + target_field: destination.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.destination_port != null" +- set: + field: server.port + value: '{{destination.port}}' + if: "ctx.destination?.port != null" +- convert: + field: server.port + target_field: server.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.port != null" +- convert: + field: juniper.srx.nat_destination_port + target_field: destination.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.nat_destination_port != null" +- set: + field: server.nat.port + value: '{{destination.nat.port}}' + if: "ctx.destination?.nat?.port != null" +- convert: + field: server.nat.port + target_field: server.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.nat?.port != null" +- convert: + field: juniper.srx.bytes_from_server + target_field: destination.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.bytes_from_server != null" +- set: + field: server.bytes + value: '{{destination.bytes}}' + if: "ctx.destination?.bytes != null" +- convert: + field: server.bytes + target_field: server.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.bytes != null" +- convert: + field: juniper.srx.packets_from_server + target_field: destination.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.packets_from_server !=null" +- set: + field: server.packets + value: '{{destination.packets}}' + if: "ctx.destination?.packets != null" +- convert: + field: server.packets + target_field: server.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.packets != null" + +############################### +## ECS Client/Source Mapping ## +############################### +- rename: + field: juniper.srx.source_address + target_field: source.ip + ignore_missing: true + if: "ctx.juniper?.srx?.source_address != null" +- set: + field: client.ip + value: '{{source.ip}}' + if: "ctx.source?.ip != null" +- rename: + field: juniper.srx.nat_source_address + target_field: source.nat.ip + ignore_missing: true + if: "ctx.juniper?.srx?.nat_source_address != null" +- rename: + field: juniper.srx.sourceip + target_field: source.ip + ignore_missing: true + if: "ctx.juniper?.srx?.sourceip != null" +- convert: + field: juniper.srx.source_port + target_field: source.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.source_port != null" +- set: + field: client.port + value: '{{source.port}}' + if: "ctx.source?.port != null" +- convert: + field: client.port + target_field: client.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.port != null" +- convert: + field: juniper.srx.nat_source_port + target_field: source.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.nat_source_port != null" +- set: + field: client.nat.port + value: '{{source.nat.port}}' + if: "ctx.source?.nat?.port != null" +- convert: + field: client.nat.port + target_field: client.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.nat?.port != null" +- convert: + field: juniper.srx.bytes_from_client + target_field: source.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.bytes_from_client != null" +- set: + field: client.bytes + value: '{{source.bytes}}' + if: "ctx.source?.bytes != null" +- convert: + field: client.bytes + target_field: client.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.bytes != null" +- convert: + field: juniper.srx.packets_from_client + target_field: source.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.packets_from_client != null" +- set: + field: client.packets + value: '{{source.packets}}' + if: "ctx.source?.packets != null" +- convert: + field: client.packets + target_field: client.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.packets != null" +- rename: + field: juniper.srx.username + target_field: source.user.name + ignore_missing: true + if: "ctx.juniper?.srx?.username != null" +- rename: + field: juniper.srx.hostname + target_field: source.address + ignore_missing: true + if: "ctx.juniper?.srx?.hostname != null" +- rename: + field: juniper.srx.client_ip + target_field: source.ip + ignore_missing: true + if: "ctx.juniper?.srx?.client_ip != null" + +###################### +## ECS URL Mapping ## +###################### +- rename: + field: juniper.srx.http_host + target_field: url.domain + ignore_missing: true + if: "ctx.juniper?.srx?.http_host != null" + +############################# +## ECS Network/Geo Mapping ## +############################# +- rename: + field: juniper.srx.protocol_id + target_field: network.iana_number + ignore_missing: true + if: "ctx.juniper?.srx?.protocol_id != null" +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true + if: "ctx.source?.geo == null" +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true + if: "ctx.destination?.geo == null" +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + field: source.nat.ip + target_field: source.geo + ignore_missing: true + if: "ctx.source?.geo == null" +- geoip: + field: destination.nat.ip + target_field: destination.geo + ignore_missing: true + if: "ctx.destination?.geo == null" +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.nat.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true + if: "ctx.source?.as == null" +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.nat.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true + if: "ctx.destination?.as == null" +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true + +############# +## Cleanup ## +############# +- remove: + field: + - juniper.srx.destination_port + - juniper.srx.nat_destination_port + - juniper.srx.bytes_from_client + - juniper.srx.packets_from_client + - juniper.srx.source_port + - juniper.srx.nat_source_port + - juniper.srx.bytes_from_server + - juniper.srx.packets_from_server + ignore_missing: true + +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/juniper/srx/ingest/utm.yml b/x-pack/filebeat/module/juniper/srx/ingest/utm.yml new file mode 100644 index 00000000000..a80e5a94d97 --- /dev/null +++ b/x-pack/filebeat/module/juniper/srx/ingest/utm.yml @@ -0,0 +1,388 @@ +description: Pipeline for parsing junipersrx firewall logs (utm pipeline) +processors: +####################### +## ECS Event Mapping ## +####################### +- set: + field: event.kind + value: event +- set: + field: event.outcome + value: success + if: "ctx.juniper?.srx?.tag != null" +- append: + field: event.category + value: network +- rename: + field: juniper.srx.urlcategory_risk + target_field: event.risk_score + ignore_missing: true + if: "ctx.juniper?.srx?.urlcategory_risk != null" +- set: + field: event.kind + value: alert + if: '["AV_VIRUS_DETECTED_MT", "WEBFILTER_URL_BLOCKED", "ANTISPAM_SPAM_DETECTED_MT", "CONTENT_FILTERING_BLOCKED_MT", "AV_VIRUS_DETECTED_MT_LS", "WEBFILTER_URL_BLOCKED_LS", "ANTISPAM_SPAM_DETECTED_MT_LS", "CONTENT_FILTERING_BLOCKED_MT_LS"].contains(ctx.juniper?.srx?.tag)' +- append: + field: event.category + value: malware + if: '["AV_VIRUS_DETECTED_MT", "WEBFILTER_URL_BLOCKED", "ANTISPAM_SPAM_DETECTED_MT", "CONTENT_FILTERING_BLOCKED_MT", "AV_VIRUS_DETECTED_MT_LS", "WEBFILTER_URL_BLOCKED_LS", "ANTISPAM_SPAM_DETECTED_MT_LS", "CONTENT_FILTERING_BLOCKED_MT_LS"].contains(ctx.juniper?.srx?.tag)' +- append: + field: event.type + value: + - info + - denied + - connection + if: '["AV_VIRUS_DETECTED_MT", "WEBFILTER_URL_BLOCKED", "ANTISPAM_SPAM_DETECTED_MT", "CONTENT_FILTERING_BLOCKED_MT", "AV_VIRUS_DETECTED_MT_LS", "WEBFILTER_URL_BLOCKED_LS", "ANTISPAM_SPAM_DETECTED_MT_LS", "CONTENT_FILTERING_BLOCKED_MT_LS"].contains(ctx.juniper?.srx?.tag)' +- append: + field: event.type + value: + - allowed + - connection + if: '!["AV_VIRUS_DETECTED_MT", "WEBFILTER_URL_BLOCKED", "ANTISPAM_SPAM_DETECTED_MT", "CONTENT_FILTERING_BLOCKED_MT", "AV_VIRUS_DETECTED_MT_LS", "WEBFILTER_URL_BLOCKED_LS", "ANTISPAM_SPAM_DETECTED_MT_LS", "CONTENT_FILTERING_BLOCKED_MT_LS"].contains(ctx.juniper?.srx?.tag)' +- set: + field: event.action + value: web_filter + if: '["WEBFILTER_URL_BLOCKED", "WEBFILTER_URL_BLOCKED_LS"].contains(ctx.juniper?.srx?.tag)' +- set: + field: event.action + value: content_filter + if: '["CONTENT_FILTERING_BLOCKED_MT", "CONTENT_FILTERING_BLOCKED_MT_LS"].contains(ctx.juniper?.srx?.tag)' +- set: + field: event.action + value: antispam_filter + if: '["ANTISPAM_SPAM_DETECTED_MT", "ANTISPAM_SPAM_DETECTED_MT_LS"].contains(ctx.juniper?.srx?.tag)' +- set: + field: event.action + value: virus_detected + if: '["AV_VIRUS_DETECTED_MT", "AV_VIRUS_DETECTED_MT_LS"].contains(ctx.juniper?.srx?.tag)' + + +#################################### +## ECS Server/Destination Mapping ## +#################################### +- rename: + field: juniper.srx.destination_address + target_field: destination.ip + ignore_missing: true + if: "ctx.juniper?.srx?.destination_address != null" +- set: + field: server.ip + value: '{{destination.ip}}' + if: "ctx.destination?.ip != null" +- rename: + field: juniper.srx.nat_destination_address + target_field: destination.nat.ip + ignore_missing: true + if: "ctx.juniper?.srx?.nat_destination_address != null" +- convert: + field: juniper.srx.destination_port + target_field: destination.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.destination_port != null" +- set: + field: server.port + value: '{{destination.port}}' + if: "ctx.destination?.port != null" +- convert: + field: server.port + target_field: server.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.port != null" +- convert: + field: juniper.srx.nat_destination_port + target_field: destination.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.nat_destination_port != null" +- set: + field: server.nat.port + value: '{{destination.nat.port}}' + if: "ctx.destination?.nat?.port != null" +- convert: + field: server.nat.port + target_field: server.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.nat?.port != null" +- convert: + field: juniper.srx.bytes_from_server + target_field: destination.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.bytes_from_server != null" +- set: + field: server.bytes + value: '{{destination.bytes}}' + if: "ctx.destination?.bytes != null" +- convert: + field: server.bytes + target_field: server.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.bytes != null" +- convert: + field: juniper.srx.packets_from_server + target_field: destination.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.packets_from_server !=null" +- set: + field: server.packets + value: '{{destination.packets}}' + if: "ctx.destination?.packets != null" +- convert: + field: server.packets + target_field: server.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.server?.packets != null" + +############################### +## ECS Client/Source Mapping ## +############################### +- rename: + field: juniper.srx.source_address + target_field: source.ip + ignore_missing: true + if: "ctx.juniper?.srx?.source_address != null" +- set: + field: client.ip + value: '{{source.ip}}' + if: "ctx.source?.ip != null" +- rename: + field: juniper.srx.nat_source_address + target_field: source.nat.ip + ignore_missing: true + if: "ctx.juniper?.srx?.nat_source_address != null" +- rename: + field: juniper.srx.sourceip + target_field: source.ip + ignore_missing: true + if: "ctx.juniper?.srx?.sourceip != null" +- convert: + field: juniper.srx.source_port + target_field: source.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.source_port != null" +- set: + field: client.port + value: '{{source.port}}' + if: "ctx.source?.port != null" +- convert: + field: client.port + target_field: client.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.port != null" +- convert: + field: juniper.srx.nat_source_port + target_field: source.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.nat_source_port != null" +- set: + field: client.nat.port + value: '{{source.nat.port}}' + if: "ctx.source?.nat?.port != null" +- convert: + field: client.nat.port + target_field: client.nat.port + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.nat?.port != null" +- convert: + field: juniper.srx.bytes_from_client + target_field: source.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.bytes_from_client != null" +- set: + field: client.bytes + value: '{{source.bytes}}' + if: "ctx.source?.bytes != null" +- convert: + field: client.bytes + target_field: client.bytes + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.bytes != null" +- convert: + field: juniper.srx.packets_from_client + target_field: source.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.juniper?.srx?.packets_from_client != null" +- set: + field: client.packets + value: '{{source.packets}}' + if: "ctx.source?.packets != null" +- convert: + field: client.packets + target_field: client.packets + type: long + ignore_failure: true + ignore_missing: true + if: "ctx.client?.packets != null" +- rename: + field: juniper.srx.username + target_field: source.user.name + ignore_missing: true + if: "ctx.juniper?.srx?.username != null" + +###################### +## ECS Rule Mapping ## +###################### +- rename: + field: juniper.srx.policy_name + target_field: rule.name + ignore_missing: true + if: "ctx.juniper?.srx?.policy_name != null" + +##################### +## ECS URL Mapping ## +##################### +- rename: + field: juniper.srx.url + target_field: url.domain + ignore_missing: true + if: "ctx.juniper?.srx?.url != null" +- rename: + field: juniper.srx.obj + target_field: url.path + ignore_missing: true + if: "ctx.juniper?.srx?.obj != null" + +###################### +## ECS File Mapping ## +###################### +- rename: + field: juniper.srx.filename + target_field: file.name + ignore_missing: true + if: "ctx.juniper?.srx?.filename != null" + +######################### +## ECS Network Mapping ## +######################### +- rename: + field: juniper.srx.protocol + target_field: network.protocol + ignore_missing: true + if: "ctx.juniper?.srx?.protocol != null" + +############################# +## ECS Network/Geo Mapping ## +############################# +- rename: + field: juniper.srx.protocol_id + target_field: network.iana_number + ignore_missing: true + if: "ctx.juniper?.srx?.protocol_id != null" +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true + if: "ctx.source?.geo == null" +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true + if: "ctx.destination?.geo == null" +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + field: source.nat.ip + target_field: source.geo + ignore_missing: true + if: "ctx.source?.geo == null" +- geoip: + field: destination.nat.ip + target_field: destination.geo + ignore_missing: true + if: "ctx.destination?.geo == null" +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.nat.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true + if: "ctx.source?.as == null" +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.nat.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true + if: "ctx.destination?.as == null" +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true + +############# +## Cleanup ## +############# +- remove: + field: + - juniper.srx.destination_port + - juniper.srx.nat_destination_port + - juniper.srx.bytes_from_client + - juniper.srx.packets_from_client + - juniper.srx.source_port + - juniper.srx.nat_source_port + - juniper.srx.bytes_from_server + - juniper.srx.packets_from_server + ignore_missing: true + +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/juniper/srx/manifest.yml b/x-pack/filebeat/module/juniper/srx/manifest.yml new file mode 100644 index 00000000000..879be66b99d --- /dev/null +++ b/x-pack/filebeat/module/juniper/srx/manifest.yml @@ -0,0 +1,26 @@ +module_version: 1.0 + +var: + - name: syslog_host + default: localhost + - name: tags + default: ["juniper.srx", "forwarded"] + - name: syslog_port + default: 9006 + - name: input + default: udp + +ingest_pipeline: + - ingest/pipeline.yml + - ingest/flow.yml + - ingest/utm.yml + - ingest/idp.yml + - ingest/ids.yml + - ingest/atp.yml + - ingest/secintel.yml + +input: config/srx.yml + +requires.processors: +- name: geoip + plugin: ingest-geoip diff --git a/x-pack/filebeat/module/juniper/srx/test/atp.log b/x-pack/filebeat/module/juniper/srx/test/atp.log new file mode 100644 index 00000000000..95c8210f038 --- /dev/null +++ b/x-pack/filebeat/module/juniper/srx/test/atp.log @@ -0,0 +1,4 @@ +<14>1 2013-12-14T16:06:59.134Z pinarello RT_AAMW - SRX_AAMW_ACTION_LOG [junos@xxx.x.x.x.x.28 http-host="www.mytest.com" file-category="executable" action="BLOCK" verdict-number="8" verdict-source=”cloud/blacklist/whitelist” source-address="10.10.10.1" source-port="57116" destination-address="187.19.188.200" destination-port="80" protocol-id="6" application="UNKNOWN" nested-application="UNKNOWN" policy-name="argon_policy" username="user1" session-id-32="50000002" source-zone-name="untrust" destination-zone-name="trust"] +<14>1 2016-09-20T10:43:30.330-07:00 host-example RT_AAMW - AAMW_MALWARE_EVENT_LOG [junos@xxxx.1.1.x.x.xxx timestamp="Thu Jun 23 09:55:38 2016" tenant-id="ABC123456" sample-sha256="ABC123" client-ip="192.0.2.0" verdict-number="9" malware-info="Eicar:TestVirus" username="admin" hostname="host.example.com"] +<11>1 2016-09-20T10:40:30.050-07:00 host-example RT_AAMW - AAMW_HOST_INFECTED_EVENT_LOG [junos@xxxx.1.1.x.x.xxx timestamp="Thu Jun 23 09:55:38 2016" tenant-id="ABC123456" client-ip="192.0.2.0" hostname="host.example.com" status="in_progress" policy-name="default" th="7" state="added" reason="malware" message="malware analysis detected host downloaded a malicious_file with score 9, sha256 ABC123"] +<165>1 2007-02-15T09:17:15.719Z aamw1 RT_AAMW - AAMW_ACTION_LOG [junos@2636.1.1.1.2.129 hostname="dummy_host" file-category="executable" verdict-number="10" malware-info="Testfile" action="PERMIT" list-hit="N/A" file-hash-lookup="FALSE" source-address="1.1.1.1" source-port="60148" destination-address="10.0.0.1" destination-port="80" protocol-id="6" application="HTTP" nested-application="N/A" policy-name="test-policy" username="N/A" roles="N/A" session-id-32="502156" source-zone-name="Inside" destination-zone-name="Outside" sample-sha256="e038b5168d9209267058112d845341cae83d92b1d1af0a10b66830acb7529494" file-name="dummy_file" url="dummy_url"] diff --git a/x-pack/filebeat/module/juniper/srx/test/atp.log-expected.json b/x-pack/filebeat/module/juniper/srx/test/atp.log-expected.json new file mode 100644 index 00000000000..4187866594e --- /dev/null +++ b/x-pack/filebeat/module/juniper/srx/test/atp.log-expected.json @@ -0,0 +1,240 @@ +[ + { + "@timestamp": "2013-12-14T14:06:59.134-02:00", + "client.ip": "10.10.10.1", + "client.port": 57116, + "destination.as.number": 28126, + "destination.as.organization.name": "BRISANET SERVICOS DE TELECOMUNICACOES LTDA", + "destination.geo.city_name": "Juazeiro do Norte", + "destination.geo.continent_name": "South America", + "destination.geo.country_iso_code": "BR", + "destination.geo.country_name": "Brazil", + "destination.geo.location.lat": -7.1467, + "destination.geo.location.lon": -39.247, + "destination.geo.region_iso_code": "BR-CE", + "destination.geo.region_name": "Ceara", + "destination.ip": "187.19.188.200", + "destination.port": 80, + "event.action": "malware_detected", + "event.category": [ + "network", + "malware" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "http-host=\"www.mytest.com\" file-category=\"executable\" action=\"BLOCK\" verdict-number=\"8\" verdict-source=\u201dcloud/blacklist/whitelist\u201d source-address=\"10.10.10.1\" source-port=\"57116\" destination-address=\"187.19.188.200\" destination-port=\"80\" protocol-id=\"6\" application=\"UNKNOWN\" nested-application=\"UNKNOWN\" policy-name=\"argon_policy\" username=\"user1\" session-id-32=\"50000002\" source-zone-name=\"untrust\" destination-zone-name=\"trust\"", + "event.outcome": "success", + "event.severity": "14", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "BLOCK", + "juniper.srx.file_category": "executable", + "juniper.srx.policy_name": "argon_policy", + "juniper.srx.process": "RT_AAMW", + "juniper.srx.session_id_32": "50000002", + "juniper.srx.tag": "SRX_AAMW_ACTION_LOG", + "juniper.srx.verdict_number": "8", + "juniper.srx.verdict_source": "\u201dcloud/blacklist/whitelist\u201d", + "log.level": "informational", + "log.offset": 0, + "network.iana_number": "6", + "observer.egress.zone": "trust", + "observer.ingress.zone": "untrust", + "observer.name": "pinarello", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.hosts": [ + "www.mytest.com" + ], + "related.ip": [ + "10.10.10.1", + "187.19.188.200" + ], + "server.ip": "187.19.188.200", + "server.port": 80, + "service.type": "juniper", + "source.ip": "10.10.10.1", + "source.port": 57116, + "source.user.name": "user1", + "tags": [ + "juniper.srx", + "forwarded" + ], + "url.domain": "www.mytest.com" + }, + { + "@timestamp": "2016-09-20T15:43:30.330-02:00", + "event.action": "malware_detected", + "event.category": [ + "network", + "malware" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "timestamp=\"Thu Jun 23 09:55:38 2016\" tenant-id=\"ABC123456\" sample-sha256=\"ABC123\" client-ip=\"192.0.2.0\" verdict-number=\"9\" malware-info=\"Eicar:TestVirus\" username=\"admin\" hostname=\"host.example.com\"", + "event.outcome": "success", + "event.severity": "14", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.malware_info": "Eicar:TestVirus", + "juniper.srx.process": "RT_AAMW", + "juniper.srx.sample_sha256": "ABC123", + "juniper.srx.tag": "AAMW_MALWARE_EVENT_LOG", + "juniper.srx.tenant_id": "ABC123456", + "juniper.srx.timestamp": "2016-06-23T09:55:38.000Z", + "juniper.srx.verdict_number": "9", + "log.level": "informational", + "log.offset": 529, + "observer.name": "host-example", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.hosts": [ + "host.example.com" + ], + "related.ip": [ + "192.0.2.0" + ], + "service.type": "juniper", + "source.domain": "host.example.com", + "source.ip": "192.0.2.0", + "source.user.name": "admin", + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2016-09-20T15:40:30.050-02:00", + "event.category": [ + "network", + "malware" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "timestamp=\"Thu Jun 23 09:55:38 2016\" tenant-id=\"ABC123456\" client-ip=\"192.0.2.0\" hostname=\"host.example.com\" status=\"in_progress\" policy-name=\"default\" th=\"7\" state=\"added\" reason=\"malware\" message=\"malware analysis detected host downloaded a malicious_file with score 9, sha256 ABC123\"", + "event.outcome": "success", + "event.severity": "11", + "event.timezone": "-02:00", + "event.type": [ + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.message": "malware analysis detected host downloaded a malicious_file with score 9, sha256 ABC123", + "juniper.srx.policy_name": "default", + "juniper.srx.process": "RT_AAMW", + "juniper.srx.reason": "malware", + "juniper.srx.state": "added", + "juniper.srx.status": "in_progress", + "juniper.srx.tag": "AAMW_HOST_INFECTED_EVENT_LOG", + "juniper.srx.tenant_id": "ABC123456", + "juniper.srx.th": "7", + "juniper.srx.timestamp": "2016-06-23T09:55:38.000Z", + "log.level": "error", + "log.offset": 835, + "observer.name": "host-example", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.hosts": [ + "host.example.com" + ], + "related.ip": [ + "192.0.2.0" + ], + "service.type": "juniper", + "source.domain": "host.example.com", + "source.ip": "192.0.2.0", + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2007-02-15T07:17:15.719-02:00", + "client.ip": "1.1.1.1", + "client.port": 60148, + "destination.ip": "10.0.0.1", + "destination.port": 80, + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.kind": "event", + "event.module": "juniper", + "event.original": "hostname=\"dummy_host\" file-category=\"executable\" verdict-number=\"10\" malware-info=\"Testfile\" action=\"PERMIT\" list-hit=\"N/A\" file-hash-lookup=\"FALSE\" source-address=\"1.1.1.1\" source-port=\"60148\" destination-address=\"10.0.0.1\" destination-port=\"80\" protocol-id=\"6\" application=\"HTTP\" nested-application=\"N/A\" policy-name=\"test-policy\" username=\"N/A\" roles=\"N/A\" session-id-32=\"502156\" source-zone-name=\"Inside\" destination-zone-name=\"Outside\" sample-sha256=\"e038b5168d9209267058112d845341cae83d92b1d1af0a10b66830acb7529494\" file-name=\"dummy_file\" url=\"dummy_url\"", + "event.outcome": "success", + "event.severity": "165", + "event.timezone": "-02:00", + "event.type": [ + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "PERMIT", + "juniper.srx.application": "HTTP", + "juniper.srx.file_category": "executable", + "juniper.srx.file_hash_lookup": "FALSE", + "juniper.srx.file_name": "dummy_file", + "juniper.srx.malware_info": "Testfile", + "juniper.srx.policy_name": "test-policy", + "juniper.srx.process": "RT_AAMW", + "juniper.srx.sample_sha256": "e038b5168d9209267058112d845341cae83d92b1d1af0a10b66830acb7529494", + "juniper.srx.session_id_32": "502156", + "juniper.srx.tag": "AAMW_ACTION_LOG", + "juniper.srx.url": "dummy_url", + "juniper.srx.verdict_number": "10", + "log.level": "notification", + "log.offset": 1235, + "network.iana_number": "6", + "observer.egress.zone": "Outside", + "observer.ingress.zone": "Inside", + "observer.name": "aamw1", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.hosts": [ + "dummy_host" + ], + "related.ip": [ + "1.1.1.1", + "10.0.0.1" + ], + "server.ip": "10.0.0.1", + "server.port": 80, + "service.type": "juniper", + "source.as.number": 13335, + "source.as.organization.name": "Cloudflare, Inc.", + "source.domain": "dummy_host", + "source.geo.continent_name": "Oceania", + "source.geo.country_iso_code": "AU", + "source.geo.country_name": "Australia", + "source.geo.location.lat": -33.494, + "source.geo.location.lon": 143.2104, + "source.ip": "1.1.1.1", + "source.port": 60148, + "tags": [ + "juniper.srx", + "forwarded" + ] + } +] \ No newline at end of file diff --git a/x-pack/filebeat/module/juniper/srx/test/flow.log b/x-pack/filebeat/module/juniper/srx/test/flow.log new file mode 100644 index 00000000000..400bceceeee --- /dev/null +++ b/x-pack/filebeat/module/juniper/srx/test/flow.log @@ -0,0 +1,25 @@ +<14>1 2019-11-14T09:37:51.184+01:00 SRX-GW1 RT_FLOW - RT_FLOW_SESSION_CREATE [junos@2636.1.1.1.2.134 source-address="10.0.0.1" source-port="594" destination-address="10.128.0.1" destination-port="10400" connection-tag="0" service-name="icmp" nat-source-address="10.0.0.1" nat-source-port="594" nat-destination-address="10.128.0.1" nat-destination-port="10400" nat-connection-tag="0" src-nat-rule-type="N/A" src-nat-rule-name="N/A" dst-nat-rule-type="N/A" dst-nat-rule-name="N/A" protocol-id="1" policy-name="vpn_trust_permit-all" source-zone-name="vpn" destination-zone-name="trust" session-id-32="6093" username="N/A" roles="N/A" packet-incoming-interface="st0.0" application="UNKNOWN" nested-application="UNKNOWN" encrypted="UNKNOWN" application-category="N/A" application-sub-category="N/A" application-risk="1" application-characteristics="N/A"] +<14>1 2019-11-14T11:12:46.573+01:00 SRX-GW1 RT_FLOW - RT_FLOW_SESSION_DENY [junos@2636.1.1.1.2.134 source-address="10.0.0.26" source-port="37233" destination-address="10.128.0.1" destination-port="161" connection-tag="0" service-name="None" protocol-id="17" icmp-type="0" policy-name="MgmtAccess-trust-cleanup" source-zone-name="trust" destination-zone-name="junos-host" application="UNKNOWN" nested-application="UNKNOWN" username="N/A" roles="N/A" packet-incoming-interface=".local..0" encrypted="No" reason="Denied by policy" session-id-32="7087" application-category="N/A" application-sub-category="N/A" application-risk="1" application-characteristics="N/A"] +<14>1 2014-05-01T08:26:51.179Z fw01 RT_FLOW - RT_FLOW_SESSION_DENY [junos@2636.1.1.1.2.39 source-address="1.2.3.4" source-port="56639" destination-address="5.6.7.8" destination-port="2003" service-name="None" protocol-id="6" icmp-type="0" policy-name="log-all-else" source-zone-name="campus" destination-zone-name="mngmt" application="UNKNOWN" nested-application="UNKNOWN" username="N/A" roles="N/A" packet-incoming-interface="reth6.0" encrypted="No "] +<14>1 2014-05-01T08:28:10.933Z fw01 RT_FLOW - RT_FLOW_SESSION_CLOSE [junos@2636.1.1.1.2.39 reason="unset" source-address="1.2.3.4" source-port="63456" destination-address="5.6.7.8" destination-port="902" service-name="None" nat-source-address="1.2.3.4" nat-source-port="63456" nat-destination-address="5.6.7.8" nat-destination-port="902" src-nat-rule-name="None" dst-nat-rule-name="None" protocol-id="17" policy-name="mngmt-to-vcenter" source-zone-name="mngmt" destination-zone-name="intra" session-id-32="15353" packets-from-client="1" bytes-from-client="94" packets-from-server="0" bytes-from-server="0" elapsed-time="60" application="UNKNOWN" nested-application="UNKNOWN" username="N/A" roles="N/A" packet-incoming-interface="reth3.5" encrypted="No "] +<14>1 2013-11-04T16:23:09.264Z cixi RT_FLOW - RT_FLOW_SESSION_CREATE [junos@2636.1.1.1.2.35 source-address="50.0.0.100" source-port="24065" destination-address="30.0.0.100" destination-port="768" service-name="icmp" nat-source-address="50.0.0.100" nat-source-port="24065" nat-destination-address="30.0.0.100" nat-destination-port="768" src-nat-rule-name="None" dst-nat-rule-name="None" protocol-id="1" policy-name="alg-policy" source-zone-name="untrust" destination-zone-name="trust" session-id-32="100000165" username="N/A" roles="N/A" packet-incoming-interface="reth2.0" application="UNKNOWN" nested-application="UNKNOWN" encrypted="UNKNOWN"] +<14>1 2010-09-30T14:55:04.323+08:00 mrpp-srx550-dut01 RT_FLOW - RT_FLOW_SESSION_CREATE [junos@2626.192.0.2.1.40 source-address="192.0.2.1" source-port="1" destination-address="198.51.100.12" destination-port="46384" service-name="icmp" nat-source-address="192.0.2.1" nat-source-port="1" nat-destination-address="18.51.100.12" nat-destination-port="46384" src-nat-rule-name="None" dst-nat-rule-name="None" protocol-id="1" policy-name="policy1" source-zone-name="trustZone" destination-zone-name="untrustZone" session-id-32="41" packet-incoming-interface="ge-0/0/1.0"] +<14>1 2010-09-30T14:55:07.188+08:00 mrpp-srx550-dut01 RT_FLOW - RT_FLOW_SESSION_CLOSE [junos@2626.192.0.2.1.40 reason="response received" source-address="192.0.2.1" source-port="1" destination-address="198.51.100.12" destination-port="46384" service-name="icmp" nat-source-address="192.0.2.1" nat-source-port="1" nat-destination-address="18.51.100.12" nat-destination-port="46384" src-nat-rule-name="None" dst-nat-rule-name="None" protocol-id="1" policy-name="policy1" source-zone-name="trustZone" destination-zone-name="untrustZone" session-id-32="41" packets-from-client="1" bytes-from-client="84" packets-from-server="1" bytes-from-server="84" elapsed-time="0" packet-incoming-interface="ge-0/0/1.0"] +<14>1 2019-04-12T14:29:06.576Z cixi RT_FLOW - RT_FLOW_SESSION_CLOSE [junos@2636.1.1.1.2.129 reason="TCP FIN" source-address="10.3.255.203" source-port="47776" destination-address="8.23.224.110" destination-port="80" connection-tag="0" service-name="junos-http" nat-source-address="10.3.136.49" nat-source-port="19162" nat-destination-address="8.23.224.110" nat-destination-port="80" nat-connection-tag="0" src-nat-rule-type="source rule" src-nat-rule-name="nat1" dst-nat-rule-type="N/A" dst-nat-rule-name="N/A" protocol-id="6" policy-name="permit_all" source-zone-name="trust" destination-zone-name="untrust" session-id-32="5" packets-from-client="6" bytes-from-client="337" packets-from-server="4" bytes-from-server="535" elapsed-time="1" application="HTTP" nested-application="UNKNOWN" username="N/A" roles="N/A" packet-incoming-interface="ge-0/0/0.0" encrypted="No" application-category="Web" application-sub-category="N/A" application-risk="4" application-characteristics="Can Leak Information;Supports File Transfer;Prone to Misuse;Known Vulnerabilities;Carrier of Malware;Capable of Tunneling;"] +<14>1 2019-04-13T14:33:06.576Z cixi RT_FLOW - RT_FLOW_SESSION_CLOSE [junos@2636.1.1.1.2.58 reason="TCP RST" source-address="192.168.2.164" source-port="53232" destination-address="172.16.1.19" destination-port="445" service-name="junos-smb" nat-source-address="192.168.2.164" nat-source-port="53232" nat-destination-address="172.16.1.19" nat-destination-port="445" src-nat-rule-name="None" dst-nat-rule-name="None" protocol-id="6" policy-name="35" source-zone-name="Trust" destination-zone-name="Trust" session-id-32="206" packets-from-client="13" bytes-from-client="4274" packets-from-server="9" bytes-from-server="1575" elapsed-time="16" application="UNKNOWN" nested-application="UNKNOWN" username="N/A" roles="N/A" packet-incoming-interface="ge-0/0/2.0"] +<14>1 2018-10-07T01:32:20.898Z TestFW2 RT_FLOW - RT_FLOW_SESSION_CLOSE [junos@2636.1.1.1.2.34 reason="idle Timeout" source-address="100.73.10.92" source-port="52890" destination-address="58.68.126.198" destination-port="53" service-name="junos-dns-udp" nat-source-address="58.78.140.131" nat-source-port="11152" nat-destination-address="58.68.126.198" nat-destination-port="53" src-nat-rule-type="source rule" src-nat-rule-name="NAT_S" dst-nat-rule-type="N/A" dst-nat-rule-name="N/A" protocol-id="17" policy-name="NAT" source-zone-name="Gi_nat" destination-zone-name="Internet" session-id-32="220368889" packets-from-client="1" bytes-from-client="72" packets-from-server="1" bytes-from-server="136" elapsed-time="8" application="UNKNOWN" nested-application="UNKNOWN" username="N/A" roles="N/A" packet-incoming-interface="reth0.108" encrypted="UNKNOWN"] +<14>1 2018-06-30T02:17:22.753Z fw0001 RT_FLOW - RT_FLOW_SESSION_CLOSE [junos@2636.1.1.1.2.41 reason="idle Timeout" source-address="192.168.255.2" source-port="62047" destination-address="8.8.8.8" destination-port="53" service-name="junos-dns-udp" nat-source-address="192.168.0.47" nat-source-port="20215" nat-destination-address="8.8.8.8" nat-destination-port="53" src-nat-rule-type="source rule" src-nat-rule-name="rule001" dst-nat-rule-type="N/A" dst-nat-rule-name="N/A" protocol-id="17" policy-name="trust-to-untrust-001" source-zone-name="trust" destination-zone-name="untrust" session-id-32="9621" packets-from-client="1" bytes-from-client="67" packets-from-server="1" bytes-from-server="116" elapsed-time="3" application="UNKNOWN" nested-application="UNKNOWN" username="N/A" roles="N/A" packet-incoming-interface="fe-0/0/1.0" encrypted="UNKNOWN"] +<14>1 2015-09-25T14:19:53.846Z VPNBox-A RT_FLOW - RT_FLOW_SESSION_CLOSE [junos@2636.1.1.1.2.36 reason="application failure or action" source-address="10.164.110.223" source-port="9057" destination-address="10.104.12.161" destination-port="21" service-name="junos-ftp" nat-source-address="10.9.1.150" nat-source-port="58020" nat-destination-address="10.12.70.1" nat-destination-port="21" src-nat-rule-name="SNAT-Policy5" dst-nat-rule-name="NAT-Policy10" protocol-id="6" policy-name="FW-FTP" source-zone-name="trust" destination-zone-name="untrust" session-id-32="24311" packets-from-client="0" bytes-from-client="0" packets-from-server="0" bytes-from-server="0" elapsed-time="1" application="UNKNOWN" nested-application="UNKNOWN" username="N/A" roles="N/A" packet-incoming-interface="reth0.0" encrypted="No "] +<14>1 2013-01-19T15:18:17.040 SRX100HM RT_FLOW - APPTRACK_SESSION_CREATE [junos@2636.1.1.1.2.41 source-address="192.168.224.30" source-port="3129" destination-address="207.17.137.56" destination-port="21" service-name="junos-ftp" application="UNKNOWN" nested-application="UNKNOWN" nat-source-address="173.167.224.7" nat-source-port="14406" nat-destination-address="207.17.137.56" nat-destination-port="21" src-nat-rule-name="1" dst-nat-rule-name="None" protocol-id="6" policy-name="General-Outbound" source-zone-name="LAN" destination-zone-name="Danger" session-id-32="5058" username="N/A" roles="N/A" encrypted="N/A"] +<14>1 2013-01-19T15:18:17.040 SRX100HM RT_FLOW - APPTRACK_SESSION_VOL_UPDATE [junos@2636.1.1.1.2.41 source-address="192.168.224.30" source-port="3129" destination-address="207.17.137.56" destination-port="21" service-name="junos-ftp" application="UNKNOWN" nested-application="UNKNOWN" nat-source-address="173.167.224.7" nat-source-port="14406" nat-destination-address="207.17.137.56" nat-destination-port="21" src-nat-rule-name="1" dst-nat-rule-name="None" protocol-id="6" policy-name="General-Outbound" source-zone-name="LAN" destination-zone-name="Danger" session-id-32="5058" packets-from-client="1" bytes-from-client="48" packets-from-server="0" bytes-from-server="0" elapsed-time="0" username="N/A" roles="N/A" encrypted="N/A"] +<14>1 2013-01-19T15:18:17.040 SRX100HM RT_FLOW - APPTRACK_SESSION_CLOSE [junos@2636.1.1.1.2.41 reason="application failure or action" source-address="192.168.224.30" source-port="3129" destination-address="207.17.137.56" destination-port="21" service-name="junos-ftp" application="FTP" nested-application="UNKNOWN" nat-source-address="173.167.224.7" nat-source-port="14406" nat-destination-address="207.17.137.56" nat-destination-port="21" src-nat-rule-name="1" dst-nat-rule-name="None" protocol-id="6" policy-name="General-Outbound" source-zone-name="LAN" destination-zone-name="Danger" session-id-32="5058" packets-from-client="3" bytes-from-client="144" packets-from-server="2" bytes-from-server="104" elapsed-time="1" username="N/A" roles="N/A" encrypted="N/A"] +<14>1 2013-01-19T15:18:18.040 SRX100HM RT_FLOW - APPTRACK_SESSION_VOL_UPDATE [junos@2636.1.1.1.2.129 source-address="4.0.0.1" source-port="33040" destination-address="5.0.0.1" destination-port="80" service-name="junos-http" application="HTTP" nested-application="FACEBOOK-SOCIALRSS" nat-source-address="4.0.0.1" nat-source-port="33040" nat-destination-address="5.0.0.1" nat-destination-port="80" src-nat-rule-name="N/A" dst-nat-rule-name="N/A" protocol-id="6" policy-name="permit-all" source-zone-name="trust" destination-zone-name="untrust" session-id-32="28" packets-from-client="371" bytes-from-client="19592" packets-from-server="584" bytes-from-server="686432" elapsed-time="60" username="user1" roles="DEPT1" encrypted="No" destination-interface-name=”st0.0” apbr-rule-type=”default”] +<14>1 2013-01-19T15:18:19.040 SRX100HM RT_FLOW - APPTRACK_SESSION_ROUTE_UPDATE [junos@2636.1.1.1.2.129 source-address="4.0.0.1" source-port="33040" destination-address="5.0.0.1" destination-port="80" service-name="junos-http" application="HTTP" nested-application="FACEBOOK-SOCIALRSS" nat-source-address="4.0.0.1" nat-source-port="33040" nat-destination-address="5.0.0.1" nat-destination-port="80" src-nat-rule-name="N/A" dst-nat-rule-name="N/A" protocol-id="6" policy-name="permit-all" source-zone-name="trust" destination-zone-name="untrust" session-id-32="28" username="user1" roles="DEPT1" encrypted="No" profile-name=”pf1” rule-name=”facebook1” routing-instance=”instance1” destination-interface-name=”st0.0” apbr-rule-type=”default”] +<14>1 2013-01-19T15:18:20.040 SRX100HM RT_FLOW - APPTRACK_SESSION_CLOSE [junos@2636.1.1.1.2.129 reason="TCP CLIENT RST" source-address="4.0.0.1" source-port="48873" destination-address="5.0.0.1" destination-port="80" service-name="junos-http" application="UNKNOWN" nested-application="UNKNOWN" nat-source-address="4.0.0.1" nat-source-port="48873" nat-destination-address="5.0.0.1" nat-destination-port="80" src-nat-rule-name="N/A" dst-nat-rule-name="N/A" protocol-id="6" policy-name="permit-all" source-zone-name="trust" destination-zone-name="untrust" session-id-32="32" packets-from-client="5" bytes-from-client="392" packets-from-server="3" bytes-from-server="646" elapsed-time="3" username="user1" roles="DEPT1" encrypted="No" destination-interface-name=”st0.0” apbr-rule-type=”default”] +<14>1 2020-11-04T16:23:09.264Z cixi RT_FLOW - RT_FLOW_SESSION_CREATE_LS [junos@2636.1.1.1.2.35 source-address="50.0.0.100" source-port="24065" destination-address="30.0.0.100" destination-port="768" service-name="icmp" nat-source-address="50.0.0.100" nat-source-port="24065" nat-destination-address="30.0.0.100" nat-destination-port="768" src-nat-rule-name="None" dst-nat-rule-name="None" protocol-id="1" policy-name="alg-policy" source-zone-name="untrust" destination-zone-name="trust" session-id-32="100000165" username="N/A" roles="N/A" packet-incoming-interface="reth2.0" application="UNKNOWN" nested-application="UNKNOWN" encrypted="UNKNOWN"] +<14>1 2020-11-14T11:12:46.573+01:00 SRX-GW1 RT_FLOW - RT_FLOW_SESSION_DENY_LS [junos@2636.1.1.1.2.134 source-address="10.0.0.26" source-port="37233" destination-address="10.128.0.1" destination-port="161" connection-tag="0" service-name="None" protocol-id="17" icmp-type="0" policy-name="MgmtAccess-trust-cleanup" source-zone-name="trust" destination-zone-name="junos-host" application="UNKNOWN" nested-application="UNKNOWN" username="N/A" roles="N/A" packet-incoming-interface=".local..0" encrypted="No" reason="Denied by policy" session-id-32="7087" application-category="N/A" application-sub-category="N/A" application-risk="1" application-characteristics="N/A"] +<14>1 2020-01-19T15:18:20.040 SRX100HM RT_FLOW - APPTRACK_SESSION_CLOSE_LS [junos@2636.1.1.1.2.129 reason="TCP CLIENT RST" source-address="4.0.0.1" source-port="48873" destination-address="5.0.0.1" destination-port="80" service-name="junos-http" application="UNKNOWN" nested-application="UNKNOWN" nat-source-address="4.0.0.1" nat-source-port="48873" nat-destination-address="5.0.0.1" nat-destination-port="80" src-nat-rule-name="N/A" dst-nat-rule-name="N/A" protocol-id="6" policy-name="permit-all" source-zone-name="trust" destination-zone-name="untrust" session-id-32="32" packets-from-client="5" bytes-from-client="392" packets-from-server="3" bytes-from-server="646" elapsed-time="3" username="user1" roles="DEPT1" encrypted="No" destination-interface-name=”st0.0” apbr-rule-type=”default”] +<14>1 2020-07-14T14:17:11.928Z SRX100HM RT_FLOW - APPTRACK_SESSION_VOL_UPDATE [junos@2636.1.1.1.2.129 source-address="10.1.1.100" source-port="58943" destination-address="46.165.154.241" destination-port="80" service-name="junos-http" application="UNKNOWN" nested-application="UNKNOWN" nat-source-address="172.19.34.100" nat-source-port="6018" nat-destination-address="46.165.154.241" nat-destination-port="80" src-nat-rule-name="our-nat-rule" dst-nat-rule-name="N/A" protocol-id="6" policy-name="default-permit" source-zone-name="trust" destination-zone-name="untrust" session-id-32="16118" packets-from-client="42" bytes-from-client="2322" packets-from-server="34" bytes-from-server="2132" elapsed-time="60" username="N/A" roles="N/A" encrypted="No" destination-interface-name="ge-0/0/0.0" category="N/A" sub-category="N/A" src-vrf-grp="N/A" dst-vrf-grp="N/A"] +<14>1 2020-07-13T16:43:05.041Z SRX100HM RT_FLOW - RT_FLOW_SESSION_CLOSE [junos@2636.1.1.1.2.129 reason="idle Timeout" source-address="10.1.1.100" source-port="64720" destination-address="91.228.167.172" destination-port="8883" connection-tag="0" service-name="None" nat-source-address="172.19.34.100" nat-source-port="24519" nat-destination-address="91.228.167.172" nat-destination-port="8883" nat-connection-tag="0" src-nat-rule-type="source rule" src-nat-rule-name="our-nat-rule" dst-nat-rule-type="N/A" dst-nat-rule-name="N/A" protocol-id="6" policy-name="default-permit" source-zone-name="trust" destination-zone-name="untrust" session-id-32="3851" packets-from-client="161" bytes-from-client="9530" packets-from-server="96" bytes-from-server="9670" elapsed-time="23755" application="UNKNOWN" nested-application="UNKNOWN" username="N/A" roles="N/A" packet-incoming-interface="ge-0/0/1.0" encrypted="UNKNOWN" application-category="N/A" application-sub-category="N/A" application-risk="1" application-characteristics="N/A" secure-web-proxy-session-type="NA" peer-session-id="0" peer-source-address="0.0.0.0" peer-source-port="0" peer-destination-address="0.0.0.0" peer-destination-port="0" hostname="NA NA" src-vrf-grp="N/A" dst-vrf-grp="N/A"] +<14>1 2020-07-13T16:12:05.530Z SRX100HM RT_FLOW - RT_FLOW_SESSION_CREATE [junos@2636.1.1.1.2.129 source-address="10.1.1.100" source-port="49583" destination-address="8.8.8.8" destination-port="53" connection-tag="0" service-name="junos-dns-udp" nat-source-address="172.19.34.100" nat-source-port="30838" nat-destination-address="8.8.8.8" nat-destination-port="53" nat-connection-tag="0" src-nat-rule-type="source rule" src-nat-rule-name="our-nat-rule" dst-nat-rule-type="N/A" dst-nat-rule-name="N/A" protocol-id="17" policy-name="default-permit" source-zone-name="trust" destination-zone-name="untrust" session-id-32="15399" username="N/A" roles="N/A" packet-incoming-interface="ge-0/0/1.0" application="UNKNOWN" nested-application="UNKNOWN" encrypted="UNKNOWN" application-category="N/A" application-sub-category="N/A" application-risk="1" application-characteristics="N/A" src-vrf-grp="N/A" dst-vrf-grp="N/A"] +<14>1 2020-07-13T16:12:05.530Z SRX100HM RT_FLOW - APPTRACK_SESSION_CLOSE [junos@2636.1.1.1.2.129 reason="Closed by junos-alg" source-address="10.1.1.100" source-port="63381" destination-address="8.8.8.8" destination-port="53" service-name="junos-dns-udp" application="UNKNOWN" nested-application="UNKNOWN" nat-source-address="172.19.34.100" nat-source-port="26764" nat-destination-address="8.8.8.8" nat-destination-port="53" src-nat-rule-name="our-nat-rule" dst-nat-rule-name="N/A" protocol-id="17" policy-name="default-permit" source-zone-name="trust" destination-zone-name="untrust" session-id-32="15361" packets-from-client="1" bytes-from-client="66" packets-from-server="1" bytes-from-server="82" elapsed-time="3" username="N/A" roles="N/A" encrypted="No" profile-name="N/A" rule-name="N/A" routing-instance="default" destination-interface-name="ge-0/0/0.0" uplink-incoming-interface-name="N/A" uplink-tx-bytes="0" uplink-rx-bytes="0" category="N/A" sub-category="N/A" apbr-policy-name="N/A" multipath-rule-name="N/A" src-vrf-grp="N/A" dst-vrf-grp="N/A"] diff --git a/x-pack/filebeat/module/juniper/srx/test/flow.log-expected.json b/x-pack/filebeat/module/juniper/srx/test/flow.log-expected.json new file mode 100644 index 00000000000..b597ed2afc5 --- /dev/null +++ b/x-pack/filebeat/module/juniper/srx/test/flow.log-expected.json @@ -0,0 +1,2013 @@ +[ + { + "@timestamp": "2019-11-14T06:37:51.184-02:00", + "client.ip": "10.0.0.1", + "client.nat.port": 594, + "client.port": 594, + "destination.ip": "10.128.0.1", + "destination.nat.ip": "10.128.0.1", + "destination.nat.port": 10400, + "destination.port": 10400, + "event.action": "flow_started", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.kind": "event", + "event.module": "juniper", + "event.original": "source-address=\"10.0.0.1\" source-port=\"594\" destination-address=\"10.128.0.1\" destination-port=\"10400\" connection-tag=\"0\" service-name=\"icmp\" nat-source-address=\"10.0.0.1\" nat-source-port=\"594\" nat-destination-address=\"10.128.0.1\" nat-destination-port=\"10400\" nat-connection-tag=\"0\" src-nat-rule-type=\"N/A\" src-nat-rule-name=\"N/A\" dst-nat-rule-type=\"N/A\" dst-nat-rule-name=\"N/A\" protocol-id=\"1\" policy-name=\"vpn_trust_permit-all\" source-zone-name=\"vpn\" destination-zone-name=\"trust\" session-id-32=\"6093\" username=\"N/A\" roles=\"N/A\" packet-incoming-interface=\"st0.0\" application=\"UNKNOWN\" nested-application=\"UNKNOWN\" encrypted=\"UNKNOWN\" application-category=\"N/A\" application-sub-category=\"N/A\" application-risk=\"1\" application-characteristics=\"N/A\"", + "event.outcome": "success", + "event.risk_score": "1", + "event.severity": "14", + "event.timezone": "-02:00", + "event.type": [ + "start", + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.connection_tag": "0", + "juniper.srx.nat_connection_tag": "0", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.service_name": "icmp", + "juniper.srx.session_id_32": "6093", + "juniper.srx.tag": "RT_FLOW_SESSION_CREATE", + "log.level": "informational", + "log.offset": 0, + "network.iana_number": "1", + "observer.egress.zone": "trust", + "observer.ingress.interface.name": "st0.0", + "observer.ingress.zone": "vpn", + "observer.name": "SRX-GW1", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "10.0.0.1", + "10.128.0.1", + "10.0.0.1", + "10.128.0.1" + ], + "rule.name": "vpn_trust_permit-all", + "server.ip": "10.128.0.1", + "server.nat.port": 10400, + "server.port": 10400, + "service.type": "juniper", + "source.ip": "10.0.0.1", + "source.nat.ip": "10.0.0.1", + "source.nat.port": 594, + "source.port": 594, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2019-11-14T08:12:46.573-02:00", + "client.ip": "10.0.0.26", + "client.port": 37233, + "destination.ip": "10.128.0.1", + "destination.port": 161, + "event.action": "flow_deny", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.kind": "event", + "event.module": "juniper", + "event.original": "source-address=\"10.0.0.26\" source-port=\"37233\" destination-address=\"10.128.0.1\" destination-port=\"161\" connection-tag=\"0\" service-name=\"None\" protocol-id=\"17\" icmp-type=\"0\" policy-name=\"MgmtAccess-trust-cleanup\" source-zone-name=\"trust\" destination-zone-name=\"junos-host\" application=\"UNKNOWN\" nested-application=\"UNKNOWN\" username=\"N/A\" roles=\"N/A\" packet-incoming-interface=\".local..0\" encrypted=\"No\" reason=\"Denied by policy\" session-id-32=\"7087\" application-category=\"N/A\" application-sub-category=\"N/A\" application-risk=\"1\" application-characteristics=\"N/A\"", + "event.outcome": "success", + "event.risk_score": "1", + "event.severity": "14", + "event.timezone": "-02:00", + "event.type": [ + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.connection_tag": "0", + "juniper.srx.encrypted": "No", + "juniper.srx.icmp_type": "0", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.reason": "Denied by policy", + "juniper.srx.session_id_32": "7087", + "juniper.srx.tag": "RT_FLOW_SESSION_DENY", + "log.level": "informational", + "log.offset": 850, + "network.iana_number": "17", + "observer.egress.zone": "junos-host", + "observer.ingress.interface.name": ".local..0", + "observer.ingress.zone": "trust", + "observer.name": "SRX-GW1", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "10.0.0.26", + "10.128.0.1" + ], + "rule.name": "MgmtAccess-trust-cleanup", + "server.ip": "10.128.0.1", + "server.port": 161, + "service.type": "juniper", + "source.ip": "10.0.0.26", + "source.port": 37233, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2014-05-01T06:26:51.179-02:00", + "client.ip": "1.2.3.4", + "client.port": 56639, + "destination.as.number": 6805, + "destination.as.organization.name": "Telefonica Germany", + "destination.geo.continent_name": "Europe", + "destination.geo.country_iso_code": "DE", + "destination.geo.country_name": "Germany", + "destination.geo.location.lat": 51.2993, + "destination.geo.location.lon": 9.491, + "destination.ip": "5.6.7.8", + "destination.port": 2003, + "event.action": "flow_deny", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.kind": "event", + "event.module": "juniper", + "event.original": "source-address=\"1.2.3.4\" source-port=\"56639\" destination-address=\"5.6.7.8\" destination-port=\"2003\" service-name=\"None\" protocol-id=\"6\" icmp-type=\"0\" policy-name=\"log-all-else\" source-zone-name=\"campus\" destination-zone-name=\"mngmt\" application=\"UNKNOWN\" nested-application=\"UNKNOWN\" username=\"N/A\" roles=\"N/A\" packet-incoming-interface=\"reth6.0\" encrypted=\"No \"", + "event.outcome": "success", + "event.severity": "14", + "event.timezone": "-02:00", + "event.type": [ + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.encrypted": "No ", + "juniper.srx.icmp_type": "0", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.tag": "RT_FLOW_SESSION_DENY", + "log.level": "informational", + "log.offset": 1513, + "network.iana_number": "6", + "observer.egress.zone": "mngmt", + "observer.ingress.interface.name": "reth6.0", + "observer.ingress.zone": "campus", + "observer.name": "fw01", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "1.2.3.4", + "5.6.7.8" + ], + "rule.name": "log-all-else", + "server.ip": "5.6.7.8", + "server.port": 2003, + "service.type": "juniper", + "source.geo.city_name": "Moscow", + "source.geo.continent_name": "Europe", + "source.geo.country_iso_code": "RU", + "source.geo.country_name": "Russia", + "source.geo.location.lat": 55.7527, + "source.geo.location.lon": 37.6172, + "source.geo.region_iso_code": "RU-MOW", + "source.geo.region_name": "Moscow", + "source.ip": "1.2.3.4", + "source.port": 56639, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2014-05-01T06:28:10.933-02:00", + "client.bytes": 94, + "client.ip": "1.2.3.4", + "client.nat.port": 63456, + "client.packets": 1, + "client.port": 63456, + "destination.as.number": 6805, + "destination.as.organization.name": "Telefonica Germany", + "destination.bytes": 0, + "destination.geo.continent_name": "Europe", + "destination.geo.country_iso_code": "DE", + "destination.geo.country_name": "Germany", + "destination.geo.location.lat": 51.2993, + "destination.geo.location.lon": 9.491, + "destination.ip": "5.6.7.8", + "destination.nat.ip": "5.6.7.8", + "destination.nat.port": 902, + "destination.packets": 0, + "destination.port": 902, + "event.action": "flow_close", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.duration": 60000000000, + "event.end": "2014-05-01T06:29:10.933-02:00", + "event.kind": "event", + "event.module": "juniper", + "event.original": "reason=\"unset\" source-address=\"1.2.3.4\" source-port=\"63456\" destination-address=\"5.6.7.8\" destination-port=\"902\" service-name=\"None\" nat-source-address=\"1.2.3.4\" nat-source-port=\"63456\" nat-destination-address=\"5.6.7.8\" nat-destination-port=\"902\" src-nat-rule-name=\"None\" dst-nat-rule-name=\"None\" protocol-id=\"17\" policy-name=\"mngmt-to-vcenter\" source-zone-name=\"mngmt\" destination-zone-name=\"intra\" session-id-32=\"15353\" packets-from-client=\"1\" bytes-from-client=\"94\" packets-from-server=\"0\" bytes-from-server=\"0\" elapsed-time=\"60\" application=\"UNKNOWN\" nested-application=\"UNKNOWN\" username=\"N/A\" roles=\"N/A\" packet-incoming-interface=\"reth3.5\" encrypted=\"No \"", + "event.outcome": "success", + "event.severity": "14", + "event.start": "2014-05-01T06:28:10.933-02:00", + "event.timezone": "-02:00", + "event.type": [ + "end", + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.encrypted": "No ", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.reason": "unset", + "juniper.srx.session_id_32": "15353", + "juniper.srx.tag": "RT_FLOW_SESSION_CLOSE", + "log.level": "informational", + "log.offset": 1966, + "network.bytes": 94, + "network.iana_number": "17", + "network.packets": 1, + "observer.egress.zone": "intra", + "observer.ingress.interface.name": "reth3.5", + "observer.ingress.zone": "mngmt", + "observer.name": "fw01", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "1.2.3.4", + "5.6.7.8", + "1.2.3.4", + "5.6.7.8" + ], + "rule.name": "mngmt-to-vcenter", + "server.bytes": 0, + "server.ip": "5.6.7.8", + "server.nat.port": 902, + "server.packets": 0, + "server.port": 902, + "service.type": "juniper", + "source.bytes": 94, + "source.geo.city_name": "Moscow", + "source.geo.continent_name": "Europe", + "source.geo.country_iso_code": "RU", + "source.geo.country_name": "Russia", + "source.geo.location.lat": 55.7527, + "source.geo.location.lon": 37.6172, + "source.geo.region_iso_code": "RU-MOW", + "source.geo.region_name": "Moscow", + "source.ip": "1.2.3.4", + "source.nat.ip": "1.2.3.4", + "source.nat.port": 63456, + "source.packets": 1, + "source.port": 63456, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2013-11-04T14:23:09.264-02:00", + "client.ip": "50.0.0.100", + "client.nat.port": 24065, + "client.port": 24065, + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.country_name": "United States", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, + "destination.ip": "30.0.0.100", + "destination.nat.ip": "30.0.0.100", + "destination.nat.port": 768, + "destination.port": 768, + "event.action": "flow_started", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.kind": "event", + "event.module": "juniper", + "event.original": "source-address=\"50.0.0.100\" source-port=\"24065\" destination-address=\"30.0.0.100\" destination-port=\"768\" service-name=\"icmp\" nat-source-address=\"50.0.0.100\" nat-source-port=\"24065\" nat-destination-address=\"30.0.0.100\" nat-destination-port=\"768\" src-nat-rule-name=\"None\" dst-nat-rule-name=\"None\" protocol-id=\"1\" policy-name=\"alg-policy\" source-zone-name=\"untrust\" destination-zone-name=\"trust\" session-id-32=\"100000165\" username=\"N/A\" roles=\"N/A\" packet-incoming-interface=\"reth2.0\" application=\"UNKNOWN\" nested-application=\"UNKNOWN\" encrypted=\"UNKNOWN\"", + "event.outcome": "success", + "event.severity": "14", + "event.timezone": "-02:00", + "event.type": [ + "start", + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.service_name": "icmp", + "juniper.srx.session_id_32": "100000165", + "juniper.srx.tag": "RT_FLOW_SESSION_CREATE", + "log.level": "informational", + "log.offset": 2721, + "network.iana_number": "1", + "observer.egress.zone": "trust", + "observer.ingress.interface.name": "reth2.0", + "observer.ingress.zone": "untrust", + "observer.name": "cixi", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "50.0.0.100", + "30.0.0.100", + "50.0.0.100", + "30.0.0.100" + ], + "rule.name": "alg-policy", + "server.ip": "30.0.0.100", + "server.nat.port": 768, + "server.port": 768, + "service.type": "juniper", + "source.geo.continent_name": "North America", + "source.geo.country_iso_code": "US", + "source.geo.country_name": "United States", + "source.geo.location.lat": 37.751, + "source.geo.location.lon": -97.822, + "source.ip": "50.0.0.100", + "source.nat.ip": "50.0.0.100", + "source.nat.port": 24065, + "source.port": 24065, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2010-09-30T04:55:04.323-02:00", + "client.ip": "192.0.2.1", + "client.nat.port": 1, + "client.port": 1, + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.country_name": "United States", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, + "destination.ip": "198.51.100.12", + "destination.nat.ip": "18.51.100.12", + "destination.nat.port": 46384, + "destination.port": 46384, + "event.action": "flow_started", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.kind": "event", + "event.module": "juniper", + "event.original": "source-address=\"192.0.2.1\" source-port=\"1\" destination-address=\"198.51.100.12\" destination-port=\"46384\" service-name=\"icmp\" nat-source-address=\"192.0.2.1\" nat-source-port=\"1\" nat-destination-address=\"18.51.100.12\" nat-destination-port=\"46384\" src-nat-rule-name=\"None\" dst-nat-rule-name=\"None\" protocol-id=\"1\" policy-name=\"policy1\" source-zone-name=\"trustZone\" destination-zone-name=\"untrustZone\" session-id-32=\"41\" packet-incoming-interface=\"ge-0/0/1.0\"", + "event.outcome": "success", + "event.severity": "14", + "event.timezone": "-02:00", + "event.type": [ + "start", + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.service_name": "icmp", + "juniper.srx.session_id_32": "41", + "juniper.srx.tag": "RT_FLOW_SESSION_CREATE", + "log.level": "informational", + "log.offset": 3366, + "network.iana_number": "1", + "observer.egress.zone": "untrustZone", + "observer.ingress.interface.name": "ge-0/0/1.0", + "observer.ingress.zone": "trustZone", + "observer.name": "mrpp-srx550-dut01", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "192.0.2.1", + "198.51.100.12", + "192.0.2.1", + "18.51.100.12" + ], + "rule.name": "policy1", + "server.ip": "198.51.100.12", + "server.nat.port": 46384, + "server.port": 46384, + "service.type": "juniper", + "source.ip": "192.0.2.1", + "source.nat.ip": "192.0.2.1", + "source.nat.port": 1, + "source.port": 1, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2010-09-30T04:55:07.188-02:00", + "client.bytes": 84, + "client.ip": "192.0.2.1", + "client.nat.port": 1, + "client.packets": 1, + "client.port": 1, + "destination.bytes": 84, + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.country_name": "United States", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, + "destination.ip": "198.51.100.12", + "destination.nat.ip": "18.51.100.12", + "destination.nat.port": 46384, + "destination.packets": 1, + "destination.port": 46384, + "event.action": "flow_close", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.duration": 0, + "event.end": "2010-09-30T04:55:07.188-02:00", + "event.kind": "event", + "event.module": "juniper", + "event.original": "reason=\"response received\" source-address=\"192.0.2.1\" source-port=\"1\" destination-address=\"198.51.100.12\" destination-port=\"46384\" service-name=\"icmp\" nat-source-address=\"192.0.2.1\" nat-source-port=\"1\" nat-destination-address=\"18.51.100.12\" nat-destination-port=\"46384\" src-nat-rule-name=\"None\" dst-nat-rule-name=\"None\" protocol-id=\"1\" policy-name=\"policy1\" source-zone-name=\"trustZone\" destination-zone-name=\"untrustZone\" session-id-32=\"41\" packets-from-client=\"1\" bytes-from-client=\"84\" packets-from-server=\"1\" bytes-from-server=\"84\" elapsed-time=\"0\" packet-incoming-interface=\"ge-0/0/1.0\"", + "event.outcome": "success", + "event.severity": "14", + "event.start": "2010-09-30T04:55:07.188-02:00", + "event.timezone": "-02:00", + "event.type": [ + "end", + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.reason": "response received", + "juniper.srx.service_name": "icmp", + "juniper.srx.session_id_32": "41", + "juniper.srx.tag": "RT_FLOW_SESSION_CLOSE", + "log.level": "informational", + "log.offset": 3933, + "network.bytes": 168, + "network.iana_number": "1", + "network.packets": 2, + "observer.egress.zone": "untrustZone", + "observer.ingress.interface.name": "ge-0/0/1.0", + "observer.ingress.zone": "trustZone", + "observer.name": "mrpp-srx550-dut01", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "192.0.2.1", + "198.51.100.12", + "192.0.2.1", + "18.51.100.12" + ], + "rule.name": "policy1", + "server.bytes": 84, + "server.ip": "198.51.100.12", + "server.nat.port": 46384, + "server.packets": 1, + "server.port": 46384, + "service.type": "juniper", + "source.bytes": 84, + "source.ip": "192.0.2.1", + "source.nat.ip": "192.0.2.1", + "source.nat.port": 1, + "source.packets": 1, + "source.port": 1, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2019-04-12T12:29:06.576-02:00", + "client.bytes": 337, + "client.ip": "10.3.255.203", + "client.nat.port": 19162, + "client.packets": 6, + "client.port": 47776, + "destination.as.number": 14627, + "destination.as.organization.name": "Vitalwerks Internet Solutions, LLC", + "destination.bytes": 535, + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.country_name": "United States", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, + "destination.ip": "8.23.224.110", + "destination.nat.ip": "8.23.224.110", + "destination.nat.port": 80, + "destination.packets": 4, + "destination.port": 80, + "event.action": "flow_close", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.duration": 1000000000, + "event.end": "2019-04-12T12:29:07.576-02:00", + "event.kind": "event", + "event.module": "juniper", + "event.original": "reason=\"TCP FIN\" source-address=\"10.3.255.203\" source-port=\"47776\" destination-address=\"8.23.224.110\" destination-port=\"80\" connection-tag=\"0\" service-name=\"junos-http\" nat-source-address=\"10.3.136.49\" nat-source-port=\"19162\" nat-destination-address=\"8.23.224.110\" nat-destination-port=\"80\" nat-connection-tag=\"0\" src-nat-rule-type=\"source rule\" src-nat-rule-name=\"nat1\" dst-nat-rule-type=\"N/A\" dst-nat-rule-name=\"N/A\" protocol-id=\"6\" policy-name=\"permit_all\" source-zone-name=\"trust\" destination-zone-name=\"untrust\" session-id-32=\"5\" packets-from-client=\"6\" bytes-from-client=\"337\" packets-from-server=\"4\" bytes-from-server=\"535\" elapsed-time=\"1\" application=\"HTTP\" nested-application=\"UNKNOWN\" username=\"N/A\" roles=\"N/A\" packet-incoming-interface=\"ge-0/0/0.0\" encrypted=\"No\" application-category=\"Web\" application-sub-category=\"N/A\" application-risk=\"4\" application-characteristics=\"Can Leak Information;Supports File Transfer;Prone to Misuse;Known Vulnerabilities;Carrier of Malware;Capable of Tunneling;\"", + "event.outcome": "success", + "event.risk_score": "4", + "event.severity": "14", + "event.start": "2019-04-12T12:29:06.576-02:00", + "event.timezone": "-02:00", + "event.type": [ + "end", + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.application": "HTTP", + "juniper.srx.application_category": "Web", + "juniper.srx.application_characteristics": "Can Leak Information;Supports File Transfer;Prone to Misuse;Known Vulnerabilities;Carrier of Malware;Capable of Tunneling;", + "juniper.srx.connection_tag": "0", + "juniper.srx.encrypted": "No", + "juniper.srx.nat_connection_tag": "0", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.reason": "TCP FIN", + "juniper.srx.service_name": "junos-http", + "juniper.srx.session_id_32": "5", + "juniper.srx.src_nat_rule_name": "nat1", + "juniper.srx.src_nat_rule_type": "source rule", + "juniper.srx.tag": "RT_FLOW_SESSION_CLOSE", + "log.level": "informational", + "log.offset": 4637, + "network.bytes": 872, + "network.iana_number": "6", + "network.packets": 10, + "observer.egress.zone": "untrust", + "observer.ingress.interface.name": "ge-0/0/0.0", + "observer.ingress.zone": "trust", + "observer.name": "cixi", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "10.3.255.203", + "8.23.224.110", + "10.3.136.49", + "8.23.224.110" + ], + "rule.name": "permit_all", + "server.bytes": 535, + "server.ip": "8.23.224.110", + "server.nat.port": 80, + "server.packets": 4, + "server.port": 80, + "service.type": "juniper", + "source.bytes": 337, + "source.ip": "10.3.255.203", + "source.nat.ip": "10.3.136.49", + "source.nat.port": 19162, + "source.packets": 6, + "source.port": 47776, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2019-04-13T12:33:06.576-02:00", + "client.bytes": 4274, + "client.ip": "192.168.2.164", + "client.nat.port": 53232, + "client.packets": 13, + "client.port": 53232, + "destination.bytes": 1575, + "destination.ip": "172.16.1.19", + "destination.nat.ip": "172.16.1.19", + "destination.nat.port": 445, + "destination.packets": 9, + "destination.port": 445, + "event.action": "flow_close", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.duration": 16000000000, + "event.end": "2019-04-13T12:33:22.576-02:00", + "event.kind": "event", + "event.module": "juniper", + "event.original": "reason=\"TCP RST\" source-address=\"192.168.2.164\" source-port=\"53232\" destination-address=\"172.16.1.19\" destination-port=\"445\" service-name=\"junos-smb\" nat-source-address=\"192.168.2.164\" nat-source-port=\"53232\" nat-destination-address=\"172.16.1.19\" nat-destination-port=\"445\" src-nat-rule-name=\"None\" dst-nat-rule-name=\"None\" protocol-id=\"6\" policy-name=\"35\" source-zone-name=\"Trust\" destination-zone-name=\"Trust\" session-id-32=\"206\" packets-from-client=\"13\" bytes-from-client=\"4274\" packets-from-server=\"9\" bytes-from-server=\"1575\" elapsed-time=\"16\" application=\"UNKNOWN\" nested-application=\"UNKNOWN\" username=\"N/A\" roles=\"N/A\" packet-incoming-interface=\"ge-0/0/2.0\"", + "event.outcome": "success", + "event.severity": "14", + "event.start": "2019-04-13T12:33:06.576-02:00", + "event.timezone": "-02:00", + "event.type": [ + "end", + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.reason": "TCP RST", + "juniper.srx.service_name": "junos-smb", + "juniper.srx.session_id_32": "206", + "juniper.srx.tag": "RT_FLOW_SESSION_CLOSE", + "log.level": "informational", + "log.offset": 5739, + "network.bytes": 5849, + "network.iana_number": "6", + "network.packets": 22, + "observer.egress.zone": "Trust", + "observer.ingress.interface.name": "ge-0/0/2.0", + "observer.ingress.zone": "Trust", + "observer.name": "cixi", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "192.168.2.164", + "172.16.1.19", + "192.168.2.164", + "172.16.1.19" + ], + "rule.name": "35", + "server.bytes": 1575, + "server.ip": "172.16.1.19", + "server.nat.port": 445, + "server.packets": 9, + "server.port": 445, + "service.type": "juniper", + "source.bytes": 4274, + "source.ip": "192.168.2.164", + "source.nat.ip": "192.168.2.164", + "source.nat.port": 53232, + "source.packets": 13, + "source.port": 53232, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2018-10-06T23:32:20.898-02:00", + "client.bytes": 72, + "client.ip": "100.73.10.92", + "client.nat.port": 11152, + "client.packets": 1, + "client.port": 52890, + "destination.as.number": 10201, + "destination.as.organization.name": "Dishnet Wireless Limited. Broadband Wireless", + "destination.bytes": 136, + "destination.geo.continent_name": "Asia", + "destination.geo.country_iso_code": "IN", + "destination.geo.country_name": "India", + "destination.geo.location.lat": 20.0, + "destination.geo.location.lon": 77.0, + "destination.ip": "58.68.126.198", + "destination.nat.ip": "58.68.126.198", + "destination.nat.port": 53, + "destination.packets": 1, + "destination.port": 53, + "event.action": "flow_close", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.duration": 8000000000, + "event.end": "2018-10-06T23:32:28.898-02:00", + "event.kind": "event", + "event.module": "juniper", + "event.original": "reason=\"idle Timeout\" source-address=\"100.73.10.92\" source-port=\"52890\" destination-address=\"58.68.126.198\" destination-port=\"53\" service-name=\"junos-dns-udp\" nat-source-address=\"58.78.140.131\" nat-source-port=\"11152\" nat-destination-address=\"58.68.126.198\" nat-destination-port=\"53\" src-nat-rule-type=\"source rule\" src-nat-rule-name=\"NAT_S\" dst-nat-rule-type=\"N/A\" dst-nat-rule-name=\"N/A\" protocol-id=\"17\" policy-name=\"NAT\" source-zone-name=\"Gi_nat\" destination-zone-name=\"Internet\" session-id-32=\"220368889\" packets-from-client=\"1\" bytes-from-client=\"72\" packets-from-server=\"1\" bytes-from-server=\"136\" elapsed-time=\"8\" application=\"UNKNOWN\" nested-application=\"UNKNOWN\" username=\"N/A\" roles=\"N/A\" packet-incoming-interface=\"reth0.108\" encrypted=\"UNKNOWN\"", + "event.outcome": "success", + "event.severity": "14", + "event.start": "2018-10-06T23:32:20.898-02:00", + "event.timezone": "-02:00", + "event.type": [ + "end", + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.reason": "idle Timeout", + "juniper.srx.service_name": "junos-dns-udp", + "juniper.srx.session_id_32": "220368889", + "juniper.srx.src_nat_rule_name": "NAT_S", + "juniper.srx.src_nat_rule_type": "source rule", + "juniper.srx.tag": "RT_FLOW_SESSION_CLOSE", + "log.level": "informational", + "log.offset": 6497, + "network.bytes": 208, + "network.iana_number": "17", + "network.packets": 2, + "observer.egress.zone": "Internet", + "observer.ingress.interface.name": "reth0.108", + "observer.ingress.zone": "Gi_nat", + "observer.name": "TestFW2", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "100.73.10.92", + "58.68.126.198", + "58.78.140.131", + "58.68.126.198" + ], + "rule.name": "NAT", + "server.bytes": 136, + "server.ip": "58.68.126.198", + "server.nat.port": 53, + "server.packets": 1, + "server.port": 53, + "service.type": "juniper", + "source.as.number": 3786, + "source.as.organization.name": "LG DACOM Corporation", + "source.bytes": 72, + "source.geo.city_name": "Seogwipo", + "source.geo.continent_name": "Asia", + "source.geo.country_iso_code": "KR", + "source.geo.country_name": "South Korea", + "source.geo.location.lat": 33.2486, + "source.geo.location.lon": 126.5628, + "source.geo.region_iso_code": "KR-49", + "source.geo.region_name": "Jeju-do", + "source.ip": "100.73.10.92", + "source.nat.ip": "58.78.140.131", + "source.nat.port": 11152, + "source.packets": 1, + "source.port": 52890, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2018-06-30T00:17:22.753-02:00", + "client.bytes": 67, + "client.ip": "192.168.255.2", + "client.nat.port": 20215, + "client.packets": 1, + "client.port": 62047, + "destination.as.number": 15169, + "destination.as.organization.name": "Google LLC", + "destination.bytes": 116, + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.country_name": "United States", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, + "destination.ip": "8.8.8.8", + "destination.nat.ip": "8.8.8.8", + "destination.nat.port": 53, + "destination.packets": 1, + "destination.port": 53, + "event.action": "flow_close", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.duration": 3000000000, + "event.end": "2018-06-30T00:17:25.753-02:00", + "event.kind": "event", + "event.module": "juniper", + "event.original": "reason=\"idle Timeout\" source-address=\"192.168.255.2\" source-port=\"62047\" destination-address=\"8.8.8.8\" destination-port=\"53\" service-name=\"junos-dns-udp\" nat-source-address=\"192.168.0.47\" nat-source-port=\"20215\" nat-destination-address=\"8.8.8.8\" nat-destination-port=\"53\" src-nat-rule-type=\"source rule\" src-nat-rule-name=\"rule001\" dst-nat-rule-type=\"N/A\" dst-nat-rule-name=\"N/A\" protocol-id=\"17\" policy-name=\"trust-to-untrust-001\" source-zone-name=\"trust\" destination-zone-name=\"untrust\" session-id-32=\"9621\" packets-from-client=\"1\" bytes-from-client=\"67\" packets-from-server=\"1\" bytes-from-server=\"116\" elapsed-time=\"3\" application=\"UNKNOWN\" nested-application=\"UNKNOWN\" username=\"N/A\" roles=\"N/A\" packet-incoming-interface=\"fe-0/0/1.0\" encrypted=\"UNKNOWN\"", + "event.outcome": "success", + "event.severity": "14", + "event.start": "2018-06-30T00:17:22.753-02:00", + "event.timezone": "-02:00", + "event.type": [ + "end", + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.reason": "idle Timeout", + "juniper.srx.service_name": "junos-dns-udp", + "juniper.srx.session_id_32": "9621", + "juniper.srx.src_nat_rule_name": "rule001", + "juniper.srx.src_nat_rule_type": "source rule", + "juniper.srx.tag": "RT_FLOW_SESSION_CLOSE", + "log.level": "informational", + "log.offset": 7350, + "network.bytes": 183, + "network.iana_number": "17", + "network.packets": 2, + "observer.egress.zone": "untrust", + "observer.ingress.interface.name": "fe-0/0/1.0", + "observer.ingress.zone": "trust", + "observer.name": "fw0001", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "192.168.255.2", + "8.8.8.8", + "192.168.0.47", + "8.8.8.8" + ], + "rule.name": "trust-to-untrust-001", + "server.bytes": 116, + "server.ip": "8.8.8.8", + "server.nat.port": 53, + "server.packets": 1, + "server.port": 53, + "service.type": "juniper", + "source.bytes": 67, + "source.ip": "192.168.255.2", + "source.nat.ip": "192.168.0.47", + "source.nat.port": 20215, + "source.packets": 1, + "source.port": 62047, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2015-09-25T12:19:53.846-02:00", + "client.bytes": 0, + "client.ip": "10.164.110.223", + "client.nat.port": 58020, + "client.packets": 0, + "client.port": 9057, + "destination.bytes": 0, + "destination.ip": "10.104.12.161", + "destination.nat.ip": "10.12.70.1", + "destination.nat.port": 21, + "destination.packets": 0, + "destination.port": 21, + "event.action": "flow_close", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.duration": 1000000000, + "event.end": "2015-09-25T12:19:54.846-02:00", + "event.kind": "event", + "event.module": "juniper", + "event.original": "reason=\"application failure or action\" source-address=\"10.164.110.223\" source-port=\"9057\" destination-address=\"10.104.12.161\" destination-port=\"21\" service-name=\"junos-ftp\" nat-source-address=\"10.9.1.150\" nat-source-port=\"58020\" nat-destination-address=\"10.12.70.1\" nat-destination-port=\"21\" src-nat-rule-name=\"SNAT-Policy5\" dst-nat-rule-name=\"NAT-Policy10\" protocol-id=\"6\" policy-name=\"FW-FTP\" source-zone-name=\"trust\" destination-zone-name=\"untrust\" session-id-32=\"24311\" packets-from-client=\"0\" bytes-from-client=\"0\" packets-from-server=\"0\" bytes-from-server=\"0\" elapsed-time=\"1\" application=\"UNKNOWN\" nested-application=\"UNKNOWN\" username=\"N/A\" roles=\"N/A\" packet-incoming-interface=\"reth0.0\" encrypted=\"No \"", + "event.outcome": "success", + "event.severity": "14", + "event.start": "2015-09-25T12:19:53.846-02:00", + "event.timezone": "-02:00", + "event.type": [ + "end", + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.dst_nat_rule_name": "NAT-Policy10", + "juniper.srx.encrypted": "No ", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.reason": "application failure or action", + "juniper.srx.service_name": "junos-ftp", + "juniper.srx.session_id_32": "24311", + "juniper.srx.src_nat_rule_name": "SNAT-Policy5", + "juniper.srx.tag": "RT_FLOW_SESSION_CLOSE", + "log.level": "informational", + "log.offset": 8203, + "network.bytes": 0, + "network.iana_number": "6", + "network.packets": 0, + "observer.egress.zone": "untrust", + "observer.ingress.interface.name": "reth0.0", + "observer.ingress.zone": "trust", + "observer.name": "VPNBox-A", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "10.164.110.223", + "10.104.12.161", + "10.9.1.150", + "10.12.70.1" + ], + "rule.name": "FW-FTP", + "server.bytes": 0, + "server.ip": "10.104.12.161", + "server.nat.port": 21, + "server.packets": 0, + "server.port": 21, + "service.type": "juniper", + "source.bytes": 0, + "source.ip": "10.164.110.223", + "source.nat.ip": "10.9.1.150", + "source.nat.port": 58020, + "source.packets": 0, + "source.port": 9057, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2013-01-19T15:18:17.040-02:00", + "client.ip": "192.168.224.30", + "client.nat.port": 14406, + "client.port": 3129, + "destination.as.number": 701, + "destination.as.organization.name": "MCI Communications Services, Inc. d/b/a Verizon Business", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.country_name": "United States", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, + "destination.ip": "207.17.137.56", + "destination.nat.ip": "207.17.137.56", + "destination.nat.port": 21, + "destination.port": 21, + "event.action": "flow_started", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.kind": "event", + "event.module": "juniper", + "event.original": "source-address=\"192.168.224.30\" source-port=\"3129\" destination-address=\"207.17.137.56\" destination-port=\"21\" service-name=\"junos-ftp\" application=\"UNKNOWN\" nested-application=\"UNKNOWN\" nat-source-address=\"173.167.224.7\" nat-source-port=\"14406\" nat-destination-address=\"207.17.137.56\" nat-destination-port=\"21\" src-nat-rule-name=\"1\" dst-nat-rule-name=\"None\" protocol-id=\"6\" policy-name=\"General-Outbound\" source-zone-name=\"LAN\" destination-zone-name=\"Danger\" session-id-32=\"5058\" username=\"N/A\" roles=\"N/A\" encrypted=\"N/A\"", + "event.outcome": "success", + "event.severity": "14", + "event.timezone": "-02:00", + "event.type": [ + "start", + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.service_name": "junos-ftp", + "juniper.srx.session_id_32": "5058", + "juniper.srx.src_nat_rule_name": "1", + "juniper.srx.tag": "APPTRACK_SESSION_CREATE", + "log.level": "informational", + "log.offset": 9012, + "network.iana_number": "6", + "observer.egress.zone": "Danger", + "observer.ingress.zone": "LAN", + "observer.name": "SRX100HM", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "192.168.224.30", + "207.17.137.56", + "173.167.224.7", + "207.17.137.56" + ], + "rule.name": "General-Outbound", + "server.ip": "207.17.137.56", + "server.nat.port": 21, + "server.port": 21, + "service.type": "juniper", + "source.as.number": 7922, + "source.as.organization.name": "Comcast Cable Communications, LLC", + "source.geo.city_name": "Plymouth", + "source.geo.continent_name": "North America", + "source.geo.country_iso_code": "US", + "source.geo.country_name": "United States", + "source.geo.location.lat": 42.3695, + "source.geo.location.lon": -83.4769, + "source.geo.region_iso_code": "US-MI", + "source.geo.region_name": "Michigan", + "source.ip": "192.168.224.30", + "source.nat.ip": "173.167.224.7", + "source.nat.port": 14406, + "source.port": 3129, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2013-01-19T15:18:17.040-02:00", + "client.bytes": 48, + "client.ip": "192.168.224.30", + "client.nat.port": 14406, + "client.packets": 1, + "client.port": 3129, + "destination.as.number": 701, + "destination.as.organization.name": "MCI Communications Services, Inc. d/b/a Verizon Business", + "destination.bytes": 0, + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.country_name": "United States", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, + "destination.ip": "207.17.137.56", + "destination.nat.ip": "207.17.137.56", + "destination.nat.port": 21, + "destination.packets": 0, + "destination.port": 21, + "event.action": "flow_started", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.duration": 0, + "event.end": "2013-01-19T15:18:17.040-02:00", + "event.kind": "event", + "event.module": "juniper", + "event.original": "source-address=\"192.168.224.30\" source-port=\"3129\" destination-address=\"207.17.137.56\" destination-port=\"21\" service-name=\"junos-ftp\" application=\"UNKNOWN\" nested-application=\"UNKNOWN\" nat-source-address=\"173.167.224.7\" nat-source-port=\"14406\" nat-destination-address=\"207.17.137.56\" nat-destination-port=\"21\" src-nat-rule-name=\"1\" dst-nat-rule-name=\"None\" protocol-id=\"6\" policy-name=\"General-Outbound\" source-zone-name=\"LAN\" destination-zone-name=\"Danger\" session-id-32=\"5058\" packets-from-client=\"1\" bytes-from-client=\"48\" packets-from-server=\"0\" bytes-from-server=\"0\" elapsed-time=\"0\" username=\"N/A\" roles=\"N/A\" encrypted=\"N/A\"", + "event.outcome": "success", + "event.severity": "14", + "event.start": "2013-01-19T15:18:17.040-02:00", + "event.timezone": "-02:00", + "event.type": [ + "start", + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.service_name": "junos-ftp", + "juniper.srx.session_id_32": "5058", + "juniper.srx.src_nat_rule_name": "1", + "juniper.srx.tag": "APPTRACK_SESSION_VOL_UPDATE", + "log.level": "informational", + "log.offset": 9631, + "network.bytes": 48, + "network.iana_number": "6", + "network.packets": 1, + "observer.egress.zone": "Danger", + "observer.ingress.zone": "LAN", + "observer.name": "SRX100HM", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "192.168.224.30", + "207.17.137.56", + "173.167.224.7", + "207.17.137.56" + ], + "rule.name": "General-Outbound", + "server.bytes": 0, + "server.ip": "207.17.137.56", + "server.nat.port": 21, + "server.packets": 0, + "server.port": 21, + "service.type": "juniper", + "source.as.number": 7922, + "source.as.organization.name": "Comcast Cable Communications, LLC", + "source.bytes": 48, + "source.geo.city_name": "Plymouth", + "source.geo.continent_name": "North America", + "source.geo.country_iso_code": "US", + "source.geo.country_name": "United States", + "source.geo.location.lat": 42.3695, + "source.geo.location.lon": -83.4769, + "source.geo.region_iso_code": "US-MI", + "source.geo.region_name": "Michigan", + "source.ip": "192.168.224.30", + "source.nat.ip": "173.167.224.7", + "source.nat.port": 14406, + "source.packets": 1, + "source.port": 3129, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2013-01-19T15:18:17.040-02:00", + "client.bytes": 144, + "client.ip": "192.168.224.30", + "client.nat.port": 14406, + "client.packets": 3, + "client.port": 3129, + "destination.as.number": 701, + "destination.as.organization.name": "MCI Communications Services, Inc. d/b/a Verizon Business", + "destination.bytes": 104, + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.country_name": "United States", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, + "destination.ip": "207.17.137.56", + "destination.nat.ip": "207.17.137.56", + "destination.nat.port": 21, + "destination.packets": 2, + "destination.port": 21, + "event.action": "flow_close", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.duration": 1000000000, + "event.end": "2013-01-19T15:18:18.040-02:00", + "event.kind": "event", + "event.module": "juniper", + "event.original": "reason=\"application failure or action\" source-address=\"192.168.224.30\" source-port=\"3129\" destination-address=\"207.17.137.56\" destination-port=\"21\" service-name=\"junos-ftp\" application=\"FTP\" nested-application=\"UNKNOWN\" nat-source-address=\"173.167.224.7\" nat-source-port=\"14406\" nat-destination-address=\"207.17.137.56\" nat-destination-port=\"21\" src-nat-rule-name=\"1\" dst-nat-rule-name=\"None\" protocol-id=\"6\" policy-name=\"General-Outbound\" source-zone-name=\"LAN\" destination-zone-name=\"Danger\" session-id-32=\"5058\" packets-from-client=\"3\" bytes-from-client=\"144\" packets-from-server=\"2\" bytes-from-server=\"104\" elapsed-time=\"1\" username=\"N/A\" roles=\"N/A\" encrypted=\"N/A\"", + "event.outcome": "success", + "event.severity": "14", + "event.start": "2013-01-19T15:18:17.040-02:00", + "event.timezone": "-02:00", + "event.type": [ + "end", + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.application": "FTP", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.reason": "application failure or action", + "juniper.srx.service_name": "junos-ftp", + "juniper.srx.session_id_32": "5058", + "juniper.srx.src_nat_rule_name": "1", + "juniper.srx.tag": "APPTRACK_SESSION_CLOSE", + "log.level": "informational", + "log.offset": 10364, + "network.bytes": 248, + "network.iana_number": "6", + "network.packets": 5, + "observer.egress.zone": "Danger", + "observer.ingress.zone": "LAN", + "observer.name": "SRX100HM", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "192.168.224.30", + "207.17.137.56", + "173.167.224.7", + "207.17.137.56" + ], + "rule.name": "General-Outbound", + "server.bytes": 104, + "server.ip": "207.17.137.56", + "server.nat.port": 21, + "server.packets": 2, + "server.port": 21, + "service.type": "juniper", + "source.as.number": 7922, + "source.as.organization.name": "Comcast Cable Communications, LLC", + "source.bytes": 144, + "source.geo.city_name": "Plymouth", + "source.geo.continent_name": "North America", + "source.geo.country_iso_code": "US", + "source.geo.country_name": "United States", + "source.geo.location.lat": 42.3695, + "source.geo.location.lon": -83.4769, + "source.geo.region_iso_code": "US-MI", + "source.geo.region_name": "Michigan", + "source.ip": "192.168.224.30", + "source.nat.ip": "173.167.224.7", + "source.nat.port": 14406, + "source.packets": 3, + "source.port": 3129, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2013-01-19T15:18:18.040-02:00", + "client.bytes": 19592, + "client.ip": "4.0.0.1", + "client.nat.port": 33040, + "client.packets": 371, + "client.port": 33040, + "destination.as.number": 29256, + "destination.as.organization.name": "Syrian Telecom", + "destination.bytes": 686432, + "destination.geo.continent_name": "Asia", + "destination.geo.country_iso_code": "SY", + "destination.geo.country_name": "Syria", + "destination.geo.location.lat": 35.0, + "destination.geo.location.lon": 38.0, + "destination.ip": "5.0.0.1", + "destination.nat.ip": "5.0.0.1", + "destination.nat.port": 80, + "destination.packets": 584, + "destination.port": 80, + "event.action": "flow_started", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.duration": 60000000000, + "event.end": "2013-01-19T15:19:18.040-02:00", + "event.kind": "event", + "event.module": "juniper", + "event.original": "source-address=\"4.0.0.1\" source-port=\"33040\" destination-address=\"5.0.0.1\" destination-port=\"80\" service-name=\"junos-http\" application=\"HTTP\" nested-application=\"FACEBOOK-SOCIALRSS\" nat-source-address=\"4.0.0.1\" nat-source-port=\"33040\" nat-destination-address=\"5.0.0.1\" nat-destination-port=\"80\" src-nat-rule-name=\"N/A\" dst-nat-rule-name=\"N/A\" protocol-id=\"6\" policy-name=\"permit-all\" source-zone-name=\"trust\" destination-zone-name=\"untrust\" session-id-32=\"28\" packets-from-client=\"371\" bytes-from-client=\"19592\" packets-from-server=\"584\" bytes-from-server=\"686432\" elapsed-time=\"60\" username=\"user1\" roles=\"DEPT1\" encrypted=\"No\" destination-interface-name=\u201dst0.0\u201d apbr-rule-type=\u201ddefault\u201d", + "event.outcome": "success", + "event.severity": "14", + "event.start": "2013-01-19T15:18:18.040-02:00", + "event.timezone": "-02:00", + "event.type": [ + "start", + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.apbr_rule_type": "\u201ddefault\u201d", + "juniper.srx.application": "HTTP", + "juniper.srx.encrypted": "No", + "juniper.srx.nested_application": "FACEBOOK-SOCIALRSS", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.roles": "DEPT1", + "juniper.srx.service_name": "junos-http", + "juniper.srx.session_id_32": "28", + "juniper.srx.tag": "APPTRACK_SESSION_VOL_UPDATE", + "log.level": "informational", + "log.offset": 11130, + "network.bytes": 706024, + "network.iana_number": "6", + "network.packets": 955, + "observer.egress.interface.name": "\u201dst0.0\u201d", + "observer.egress.zone": "untrust", + "observer.ingress.zone": "trust", + "observer.name": "SRX100HM", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "4.0.0.1", + "5.0.0.1", + "4.0.0.1", + "5.0.0.1" + ], + "rule.name": "permit-all", + "server.bytes": 686432, + "server.ip": "5.0.0.1", + "server.nat.port": 80, + "server.packets": 584, + "server.port": 80, + "service.type": "juniper", + "source.as.number": 3356, + "source.as.organization.name": "Level 3 Parent, LLC", + "source.bytes": 19592, + "source.geo.continent_name": "North America", + "source.geo.country_iso_code": "US", + "source.geo.country_name": "United States", + "source.geo.location.lat": 37.751, + "source.geo.location.lon": -97.822, + "source.ip": "4.0.0.1", + "source.nat.ip": "4.0.0.1", + "source.nat.port": 33040, + "source.packets": 371, + "source.port": 33040, + "source.user.name": "user1", + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2013-01-19T15:18:19.040-02:00", + "client.ip": "4.0.0.1", + "client.nat.port": 33040, + "client.port": 33040, + "destination.as.number": 29256, + "destination.as.organization.name": "Syrian Telecom", + "destination.geo.continent_name": "Asia", + "destination.geo.country_iso_code": "SY", + "destination.geo.country_name": "Syria", + "destination.geo.location.lat": 35.0, + "destination.geo.location.lon": 38.0, + "destination.ip": "5.0.0.1", + "destination.nat.ip": "5.0.0.1", + "destination.nat.port": 80, + "destination.port": 80, + "event.action": "flow_started", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.kind": "event", + "event.module": "juniper", + "event.original": "source-address=\"4.0.0.1\" source-port=\"33040\" destination-address=\"5.0.0.1\" destination-port=\"80\" service-name=\"junos-http\" application=\"HTTP\" nested-application=\"FACEBOOK-SOCIALRSS\" nat-source-address=\"4.0.0.1\" nat-source-port=\"33040\" nat-destination-address=\"5.0.0.1\" nat-destination-port=\"80\" src-nat-rule-name=\"N/A\" dst-nat-rule-name=\"N/A\" protocol-id=\"6\" policy-name=\"permit-all\" source-zone-name=\"trust\" destination-zone-name=\"untrust\" session-id-32=\"28\" username=\"user1\" roles=\"DEPT1\" encrypted=\"No\" profile-name=\u201dpf1\u201d rule-name=\u201dfacebook1\u201d routing-instance=\u201dinstance1\u201d destination-interface-name=\u201dst0.0\u201d apbr-rule-type=\u201ddefault\u201d", + "event.outcome": "success", + "event.severity": "14", + "event.timezone": "-02:00", + "event.type": [ + "start", + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.apbr_rule_type": "\u201ddefault\u201d", + "juniper.srx.application": "HTTP", + "juniper.srx.encrypted": "No", + "juniper.srx.nested_application": "FACEBOOK-SOCIALRSS", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.profile_name": "\u201dpf1\u201d", + "juniper.srx.roles": "DEPT1", + "juniper.srx.routing_instance": "\u201dinstance1\u201d", + "juniper.srx.rule_name": "\u201dfacebook1\u201d", + "juniper.srx.service_name": "junos-http", + "juniper.srx.session_id_32": "28", + "juniper.srx.tag": "APPTRACK_SESSION_ROUTE_UPDATE", + "log.level": "informational", + "log.offset": 11929, + "network.iana_number": "6", + "observer.egress.interface.name": "\u201dst0.0\u201d", + "observer.egress.zone": "untrust", + "observer.ingress.zone": "trust", + "observer.name": "SRX100HM", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "4.0.0.1", + "5.0.0.1", + "4.0.0.1", + "5.0.0.1" + ], + "rule.name": "permit-all", + "server.ip": "5.0.0.1", + "server.nat.port": 80, + "server.port": 80, + "service.type": "juniper", + "source.as.number": 3356, + "source.as.organization.name": "Level 3 Parent, LLC", + "source.geo.continent_name": "North America", + "source.geo.country_iso_code": "US", + "source.geo.country_name": "United States", + "source.geo.location.lat": 37.751, + "source.geo.location.lon": -97.822, + "source.ip": "4.0.0.1", + "source.nat.ip": "4.0.0.1", + "source.nat.port": 33040, + "source.port": 33040, + "source.user.name": "user1", + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2013-01-19T15:18:20.040-02:00", + "client.bytes": 392, + "client.ip": "4.0.0.1", + "client.nat.port": 48873, + "client.packets": 5, + "client.port": 48873, + "destination.as.number": 29256, + "destination.as.organization.name": "Syrian Telecom", + "destination.bytes": 646, + "destination.geo.continent_name": "Asia", + "destination.geo.country_iso_code": "SY", + "destination.geo.country_name": "Syria", + "destination.geo.location.lat": 35.0, + "destination.geo.location.lon": 38.0, + "destination.ip": "5.0.0.1", + "destination.nat.ip": "5.0.0.1", + "destination.nat.port": 80, + "destination.packets": 3, + "destination.port": 80, + "event.action": "flow_close", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.duration": 3000000000, + "event.end": "2013-01-19T15:18:23.040-02:00", + "event.kind": "event", + "event.module": "juniper", + "event.original": "reason=\"TCP CLIENT RST\" source-address=\"4.0.0.1\" source-port=\"48873\" destination-address=\"5.0.0.1\" destination-port=\"80\" service-name=\"junos-http\" application=\"UNKNOWN\" nested-application=\"UNKNOWN\" nat-source-address=\"4.0.0.1\" nat-source-port=\"48873\" nat-destination-address=\"5.0.0.1\" nat-destination-port=\"80\" src-nat-rule-name=\"N/A\" dst-nat-rule-name=\"N/A\" protocol-id=\"6\" policy-name=\"permit-all\" source-zone-name=\"trust\" destination-zone-name=\"untrust\" session-id-32=\"32\" packets-from-client=\"5\" bytes-from-client=\"392\" packets-from-server=\"3\" bytes-from-server=\"646\" elapsed-time=\"3\" username=\"user1\" roles=\"DEPT1\" encrypted=\"No\" destination-interface-name=\u201dst0.0\u201d apbr-rule-type=\u201ddefault\u201d", + "event.outcome": "success", + "event.severity": "14", + "event.start": "2013-01-19T15:18:20.040-02:00", + "event.timezone": "-02:00", + "event.type": [ + "end", + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.apbr_rule_type": "\u201ddefault\u201d", + "juniper.srx.encrypted": "No", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.reason": "TCP CLIENT RST", + "juniper.srx.roles": "DEPT1", + "juniper.srx.service_name": "junos-http", + "juniper.srx.session_id_32": "32", + "juniper.srx.tag": "APPTRACK_SESSION_CLOSE", + "log.level": "informational", + "log.offset": 12689, + "network.bytes": 1038, + "network.iana_number": "6", + "network.packets": 8, + "observer.egress.interface.name": "\u201dst0.0\u201d", + "observer.egress.zone": "untrust", + "observer.ingress.zone": "trust", + "observer.name": "SRX100HM", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "4.0.0.1", + "5.0.0.1", + "4.0.0.1", + "5.0.0.1" + ], + "rule.name": "permit-all", + "server.bytes": 646, + "server.ip": "5.0.0.1", + "server.nat.port": 80, + "server.packets": 3, + "server.port": 80, + "service.type": "juniper", + "source.as.number": 3356, + "source.as.organization.name": "Level 3 Parent, LLC", + "source.bytes": 392, + "source.geo.continent_name": "North America", + "source.geo.country_iso_code": "US", + "source.geo.country_name": "United States", + "source.geo.location.lat": 37.751, + "source.geo.location.lon": -97.822, + "source.ip": "4.0.0.1", + "source.nat.ip": "4.0.0.1", + "source.nat.port": 48873, + "source.packets": 5, + "source.port": 48873, + "source.user.name": "user1", + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2020-11-04T14:23:09.264-02:00", + "client.ip": "50.0.0.100", + "client.nat.port": 24065, + "client.port": 24065, + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.country_name": "United States", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, + "destination.ip": "30.0.0.100", + "destination.nat.ip": "30.0.0.100", + "destination.nat.port": 768, + "destination.port": 768, + "event.action": "flow_started", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.kind": "event", + "event.module": "juniper", + "event.original": "source-address=\"50.0.0.100\" source-port=\"24065\" destination-address=\"30.0.0.100\" destination-port=\"768\" service-name=\"icmp\" nat-source-address=\"50.0.0.100\" nat-source-port=\"24065\" nat-destination-address=\"30.0.0.100\" nat-destination-port=\"768\" src-nat-rule-name=\"None\" dst-nat-rule-name=\"None\" protocol-id=\"1\" policy-name=\"alg-policy\" source-zone-name=\"untrust\" destination-zone-name=\"trust\" session-id-32=\"100000165\" username=\"N/A\" roles=\"N/A\" packet-incoming-interface=\"reth2.0\" application=\"UNKNOWN\" nested-application=\"UNKNOWN\" encrypted=\"UNKNOWN\"", + "event.outcome": "success", + "event.severity": "14", + "event.timezone": "-02:00", + "event.type": [ + "start", + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.service_name": "icmp", + "juniper.srx.session_id_32": "100000165", + "juniper.srx.tag": "RT_FLOW_SESSION_CREATE_LS", + "log.level": "informational", + "log.offset": 13489, + "network.iana_number": "1", + "observer.egress.zone": "trust", + "observer.ingress.interface.name": "reth2.0", + "observer.ingress.zone": "untrust", + "observer.name": "cixi", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "50.0.0.100", + "30.0.0.100", + "50.0.0.100", + "30.0.0.100" + ], + "rule.name": "alg-policy", + "server.ip": "30.0.0.100", + "server.nat.port": 768, + "server.port": 768, + "service.type": "juniper", + "source.geo.continent_name": "North America", + "source.geo.country_iso_code": "US", + "source.geo.country_name": "United States", + "source.geo.location.lat": 37.751, + "source.geo.location.lon": -97.822, + "source.ip": "50.0.0.100", + "source.nat.ip": "50.0.0.100", + "source.nat.port": 24065, + "source.port": 24065, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2020-11-14T08:12:46.573-02:00", + "client.ip": "10.0.0.26", + "client.port": 37233, + "destination.ip": "10.128.0.1", + "destination.port": 161, + "event.action": "flow_deny", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.kind": "event", + "event.module": "juniper", + "event.original": "source-address=\"10.0.0.26\" source-port=\"37233\" destination-address=\"10.128.0.1\" destination-port=\"161\" connection-tag=\"0\" service-name=\"None\" protocol-id=\"17\" icmp-type=\"0\" policy-name=\"MgmtAccess-trust-cleanup\" source-zone-name=\"trust\" destination-zone-name=\"junos-host\" application=\"UNKNOWN\" nested-application=\"UNKNOWN\" username=\"N/A\" roles=\"N/A\" packet-incoming-interface=\".local..0\" encrypted=\"No\" reason=\"Denied by policy\" session-id-32=\"7087\" application-category=\"N/A\" application-sub-category=\"N/A\" application-risk=\"1\" application-characteristics=\"N/A\"", + "event.outcome": "success", + "event.risk_score": "1", + "event.severity": "14", + "event.timezone": "-02:00", + "event.type": [ + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.connection_tag": "0", + "juniper.srx.encrypted": "No", + "juniper.srx.icmp_type": "0", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.reason": "Denied by policy", + "juniper.srx.session_id_32": "7087", + "juniper.srx.tag": "RT_FLOW_SESSION_DENY_LS", + "log.level": "informational", + "log.offset": 14137, + "network.iana_number": "17", + "observer.egress.zone": "junos-host", + "observer.ingress.interface.name": ".local..0", + "observer.ingress.zone": "trust", + "observer.name": "SRX-GW1", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "10.0.0.26", + "10.128.0.1" + ], + "rule.name": "MgmtAccess-trust-cleanup", + "server.ip": "10.128.0.1", + "server.port": 161, + "service.type": "juniper", + "source.ip": "10.0.0.26", + "source.port": 37233, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2020-01-19T15:18:20.040-02:00", + "client.bytes": 392, + "client.ip": "4.0.0.1", + "client.nat.port": 48873, + "client.packets": 5, + "client.port": 48873, + "destination.as.number": 29256, + "destination.as.organization.name": "Syrian Telecom", + "destination.bytes": 646, + "destination.geo.continent_name": "Asia", + "destination.geo.country_iso_code": "SY", + "destination.geo.country_name": "Syria", + "destination.geo.location.lat": 35.0, + "destination.geo.location.lon": 38.0, + "destination.ip": "5.0.0.1", + "destination.nat.ip": "5.0.0.1", + "destination.nat.port": 80, + "destination.packets": 3, + "destination.port": 80, + "event.action": "flow_close", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.duration": 3000000000, + "event.end": "2020-01-19T15:18:23.040-02:00", + "event.kind": "event", + "event.module": "juniper", + "event.original": "reason=\"TCP CLIENT RST\" source-address=\"4.0.0.1\" source-port=\"48873\" destination-address=\"5.0.0.1\" destination-port=\"80\" service-name=\"junos-http\" application=\"UNKNOWN\" nested-application=\"UNKNOWN\" nat-source-address=\"4.0.0.1\" nat-source-port=\"48873\" nat-destination-address=\"5.0.0.1\" nat-destination-port=\"80\" src-nat-rule-name=\"N/A\" dst-nat-rule-name=\"N/A\" protocol-id=\"6\" policy-name=\"permit-all\" source-zone-name=\"trust\" destination-zone-name=\"untrust\" session-id-32=\"32\" packets-from-client=\"5\" bytes-from-client=\"392\" packets-from-server=\"3\" bytes-from-server=\"646\" elapsed-time=\"3\" username=\"user1\" roles=\"DEPT1\" encrypted=\"No\" destination-interface-name=\u201dst0.0\u201d apbr-rule-type=\u201ddefault\u201d", + "event.outcome": "success", + "event.severity": "14", + "event.start": "2020-01-19T15:18:20.040-02:00", + "event.timezone": "-02:00", + "event.type": [ + "end", + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.apbr_rule_type": "\u201ddefault\u201d", + "juniper.srx.encrypted": "No", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.reason": "TCP CLIENT RST", + "juniper.srx.roles": "DEPT1", + "juniper.srx.service_name": "junos-http", + "juniper.srx.session_id_32": "32", + "juniper.srx.tag": "APPTRACK_SESSION_CLOSE_LS", + "log.level": "informational", + "log.offset": 14803, + "network.bytes": 1038, + "network.iana_number": "6", + "network.packets": 8, + "observer.egress.interface.name": "\u201dst0.0\u201d", + "observer.egress.zone": "untrust", + "observer.ingress.zone": "trust", + "observer.name": "SRX100HM", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "4.0.0.1", + "5.0.0.1", + "4.0.0.1", + "5.0.0.1" + ], + "rule.name": "permit-all", + "server.bytes": 646, + "server.ip": "5.0.0.1", + "server.nat.port": 80, + "server.packets": 3, + "server.port": 80, + "service.type": "juniper", + "source.as.number": 3356, + "source.as.organization.name": "Level 3 Parent, LLC", + "source.bytes": 392, + "source.geo.continent_name": "North America", + "source.geo.country_iso_code": "US", + "source.geo.country_name": "United States", + "source.geo.location.lat": 37.751, + "source.geo.location.lon": -97.822, + "source.ip": "4.0.0.1", + "source.nat.ip": "4.0.0.1", + "source.nat.port": 48873, + "source.packets": 5, + "source.port": 48873, + "source.user.name": "user1", + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2020-07-14T12:17:11.928-02:00", + "client.bytes": 2322, + "client.ip": "10.1.1.100", + "client.nat.port": 6018, + "client.packets": 42, + "client.port": 58943, + "destination.as.number": 42652, + "destination.as.organization.name": "inexio Informationstechnologie und Telekommunikation Gmbh", + "destination.bytes": 2132, + "destination.geo.city_name": "Philippsburg", + "destination.geo.continent_name": "Europe", + "destination.geo.country_iso_code": "DE", + "destination.geo.country_name": "Germany", + "destination.geo.location.lat": 49.2317, + "destination.geo.location.lon": 8.4607, + "destination.geo.region_iso_code": "DE-BW", + "destination.geo.region_name": "Baden-W\u00fcrttemberg", + "destination.ip": "46.165.154.241", + "destination.nat.ip": "46.165.154.241", + "destination.nat.port": 80, + "destination.packets": 34, + "destination.port": 80, + "event.action": "flow_started", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.duration": 60000000000, + "event.end": "2020-07-14T12:18:11.928-02:00", + "event.kind": "event", + "event.module": "juniper", + "event.original": "source-address=\"10.1.1.100\" source-port=\"58943\" destination-address=\"46.165.154.241\" destination-port=\"80\" service-name=\"junos-http\" application=\"UNKNOWN\" nested-application=\"UNKNOWN\" nat-source-address=\"172.19.34.100\" nat-source-port=\"6018\" nat-destination-address=\"46.165.154.241\" nat-destination-port=\"80\" src-nat-rule-name=\"our-nat-rule\" dst-nat-rule-name=\"N/A\" protocol-id=\"6\" policy-name=\"default-permit\" source-zone-name=\"trust\" destination-zone-name=\"untrust\" session-id-32=\"16118\" packets-from-client=\"42\" bytes-from-client=\"2322\" packets-from-server=\"34\" bytes-from-server=\"2132\" elapsed-time=\"60\" username=\"N/A\" roles=\"N/A\" encrypted=\"No\" destination-interface-name=\"ge-0/0/0.0\" category=\"N/A\" sub-category=\"N/A\" src-vrf-grp=\"N/A\" dst-vrf-grp=\"N/A\"", + "event.outcome": "success", + "event.severity": "14", + "event.start": "2020-07-14T12:17:11.928-02:00", + "event.timezone": "-02:00", + "event.type": [ + "start", + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.encrypted": "No", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.service_name": "junos-http", + "juniper.srx.session_id_32": "16118", + "juniper.srx.src_nat_rule_name": "our-nat-rule", + "juniper.srx.tag": "APPTRACK_SESSION_VOL_UPDATE", + "log.level": "informational", + "log.offset": 15606, + "network.bytes": 4454, + "network.iana_number": "6", + "network.packets": 76, + "observer.egress.interface.name": "ge-0/0/0.0", + "observer.egress.zone": "untrust", + "observer.ingress.zone": "trust", + "observer.name": "SRX100HM", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "10.1.1.100", + "46.165.154.241", + "172.19.34.100", + "46.165.154.241" + ], + "rule.name": "default-permit", + "server.bytes": 2132, + "server.ip": "46.165.154.241", + "server.nat.port": 80, + "server.packets": 34, + "server.port": 80, + "service.type": "juniper", + "source.bytes": 2322, + "source.ip": "10.1.1.100", + "source.nat.ip": "172.19.34.100", + "source.nat.port": 6018, + "source.packets": 42, + "source.port": 58943, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2020-07-13T14:43:05.041-02:00", + "client.bytes": 9530, + "client.ip": "10.1.1.100", + "client.nat.port": 24519, + "client.packets": 161, + "client.port": 64720, + "destination.as.number": 50881, + "destination.as.organization.name": "ESET, spol. s r.o.", + "destination.bytes": 9670, + "destination.geo.city_name": "Bratislava", + "destination.geo.continent_name": "Europe", + "destination.geo.country_iso_code": "SK", + "destination.geo.country_name": "Slovakia", + "destination.geo.location.lat": 48.15, + "destination.geo.location.lon": 17.1078, + "destination.geo.region_iso_code": "SK-BL", + "destination.geo.region_name": "Bratislava", + "destination.ip": "91.228.167.172", + "destination.nat.ip": "91.228.167.172", + "destination.nat.port": 8883, + "destination.packets": 96, + "destination.port": 8883, + "event.action": "flow_close", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.duration": 23755000000000, + "event.end": "2020-07-13T21:19:00.041-02:00", + "event.kind": "event", + "event.module": "juniper", + "event.original": "reason=\"idle Timeout\" source-address=\"10.1.1.100\" source-port=\"64720\" destination-address=\"91.228.167.172\" destination-port=\"8883\" connection-tag=\"0\" service-name=\"None\" nat-source-address=\"172.19.34.100\" nat-source-port=\"24519\" nat-destination-address=\"91.228.167.172\" nat-destination-port=\"8883\" nat-connection-tag=\"0\" src-nat-rule-type=\"source rule\" src-nat-rule-name=\"our-nat-rule\" dst-nat-rule-type=\"N/A\" dst-nat-rule-name=\"N/A\" protocol-id=\"6\" policy-name=\"default-permit\" source-zone-name=\"trust\" destination-zone-name=\"untrust\" session-id-32=\"3851\" packets-from-client=\"161\" bytes-from-client=\"9530\" packets-from-server=\"96\" bytes-from-server=\"9670\" elapsed-time=\"23755\" application=\"UNKNOWN\" nested-application=\"UNKNOWN\" username=\"N/A\" roles=\"N/A\" packet-incoming-interface=\"ge-0/0/1.0\" encrypted=\"UNKNOWN\" application-category=\"N/A\" application-sub-category=\"N/A\" application-risk=\"1\" application-characteristics=\"N/A\" secure-web-proxy-session-type=\"NA\" peer-session-id=\"0\" peer-source-address=\"0.0.0.0\" peer-source-port=\"0\" peer-destination-address=\"0.0.0.0\" peer-destination-port=\"0\" hostname=\"NA NA\" src-vrf-grp=\"N/A\" dst-vrf-grp=\"N/A\"", + "event.outcome": "success", + "event.risk_score": "1", + "event.severity": "14", + "event.start": "2020-07-13T14:43:05.041-02:00", + "event.timezone": "-02:00", + "event.type": [ + "end", + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.connection_tag": "0", + "juniper.srx.hostname": "NA NA", + "juniper.srx.nat_connection_tag": "0", + "juniper.srx.peer_destination_address": "0.0.0.0", + "juniper.srx.peer_destination_port": "0", + "juniper.srx.peer_session_id": "0", + "juniper.srx.peer_source_address": "0.0.0.0", + "juniper.srx.peer_source_port": "0", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.reason": "idle Timeout", + "juniper.srx.secure_web_proxy_session_type": "NA", + "juniper.srx.session_id_32": "3851", + "juniper.srx.src_nat_rule_name": "our-nat-rule", + "juniper.srx.src_nat_rule_type": "source rule", + "juniper.srx.tag": "RT_FLOW_SESSION_CLOSE", + "log.level": "informational", + "log.offset": 16469, + "network.bytes": 19200, + "network.iana_number": "6", + "network.packets": 257, + "observer.egress.zone": "untrust", + "observer.ingress.interface.name": "ge-0/0/1.0", + "observer.ingress.zone": "trust", + "observer.name": "SRX100HM", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "10.1.1.100", + "91.228.167.172", + "172.19.34.100", + "91.228.167.172" + ], + "rule.name": "default-permit", + "server.bytes": 9670, + "server.ip": "91.228.167.172", + "server.nat.port": 8883, + "server.packets": 96, + "server.port": 8883, + "service.type": "juniper", + "source.bytes": 9530, + "source.ip": "10.1.1.100", + "source.nat.ip": "172.19.34.100", + "source.nat.port": 24519, + "source.packets": 161, + "source.port": 64720, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2020-07-13T14:12:05.530-02:00", + "client.ip": "10.1.1.100", + "client.nat.port": 30838, + "client.port": 49583, + "destination.as.number": 15169, + "destination.as.organization.name": "Google LLC", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.country_name": "United States", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, + "destination.ip": "8.8.8.8", + "destination.nat.ip": "8.8.8.8", + "destination.nat.port": 53, + "destination.port": 53, + "event.action": "flow_started", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.kind": "event", + "event.module": "juniper", + "event.original": "source-address=\"10.1.1.100\" source-port=\"49583\" destination-address=\"8.8.8.8\" destination-port=\"53\" connection-tag=\"0\" service-name=\"junos-dns-udp\" nat-source-address=\"172.19.34.100\" nat-source-port=\"30838\" nat-destination-address=\"8.8.8.8\" nat-destination-port=\"53\" nat-connection-tag=\"0\" src-nat-rule-type=\"source rule\" src-nat-rule-name=\"our-nat-rule\" dst-nat-rule-type=\"N/A\" dst-nat-rule-name=\"N/A\" protocol-id=\"17\" policy-name=\"default-permit\" source-zone-name=\"trust\" destination-zone-name=\"untrust\" session-id-32=\"15399\" username=\"N/A\" roles=\"N/A\" packet-incoming-interface=\"ge-0/0/1.0\" application=\"UNKNOWN\" nested-application=\"UNKNOWN\" encrypted=\"UNKNOWN\" application-category=\"N/A\" application-sub-category=\"N/A\" application-risk=\"1\" application-characteristics=\"N/A\" src-vrf-grp=\"N/A\" dst-vrf-grp=\"N/A\"", + "event.outcome": "success", + "event.risk_score": "1", + "event.severity": "14", + "event.timezone": "-02:00", + "event.type": [ + "start", + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.connection_tag": "0", + "juniper.srx.nat_connection_tag": "0", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.service_name": "junos-dns-udp", + "juniper.srx.session_id_32": "15399", + "juniper.srx.src_nat_rule_name": "our-nat-rule", + "juniper.srx.src_nat_rule_type": "source rule", + "juniper.srx.tag": "RT_FLOW_SESSION_CREATE", + "log.level": "informational", + "log.offset": 17715, + "network.iana_number": "17", + "observer.egress.zone": "untrust", + "observer.ingress.interface.name": "ge-0/0/1.0", + "observer.ingress.zone": "trust", + "observer.name": "SRX100HM", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "10.1.1.100", + "8.8.8.8", + "172.19.34.100", + "8.8.8.8" + ], + "rule.name": "default-permit", + "server.ip": "8.8.8.8", + "server.nat.port": 53, + "server.port": 53, + "service.type": "juniper", + "source.ip": "10.1.1.100", + "source.nat.ip": "172.19.34.100", + "source.nat.port": 30838, + "source.port": 49583, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2020-07-13T14:12:05.530-02:00", + "client.bytes": 66, + "client.ip": "10.1.1.100", + "client.nat.port": 26764, + "client.packets": 1, + "client.port": 63381, + "destination.as.number": 15169, + "destination.as.organization.name": "Google LLC", + "destination.bytes": 82, + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.country_name": "United States", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, + "destination.ip": "8.8.8.8", + "destination.nat.ip": "8.8.8.8", + "destination.nat.port": 53, + "destination.packets": 1, + "destination.port": 53, + "event.action": "flow_close", + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.duration": 3000000000, + "event.end": "2020-07-13T14:12:08.530-02:00", + "event.kind": "event", + "event.module": "juniper", + "event.original": "reason=\"Closed by junos-alg\" source-address=\"10.1.1.100\" source-port=\"63381\" destination-address=\"8.8.8.8\" destination-port=\"53\" service-name=\"junos-dns-udp\" application=\"UNKNOWN\" nested-application=\"UNKNOWN\" nat-source-address=\"172.19.34.100\" nat-source-port=\"26764\" nat-destination-address=\"8.8.8.8\" nat-destination-port=\"53\" src-nat-rule-name=\"our-nat-rule\" dst-nat-rule-name=\"N/A\" protocol-id=\"17\" policy-name=\"default-permit\" source-zone-name=\"trust\" destination-zone-name=\"untrust\" session-id-32=\"15361\" packets-from-client=\"1\" bytes-from-client=\"66\" packets-from-server=\"1\" bytes-from-server=\"82\" elapsed-time=\"3\" username=\"N/A\" roles=\"N/A\" encrypted=\"No\" profile-name=\"N/A\" rule-name=\"N/A\" routing-instance=\"default\" destination-interface-name=\"ge-0/0/0.0\" uplink-incoming-interface-name=\"N/A\" uplink-tx-bytes=\"0\" uplink-rx-bytes=\"0\" category=\"N/A\" sub-category=\"N/A\" apbr-policy-name=\"N/A\" multipath-rule-name=\"N/A\" src-vrf-grp=\"N/A\" dst-vrf-grp=\"N/A\"", + "event.outcome": "success", + "event.severity": "14", + "event.start": "2020-07-13T14:12:05.530-02:00", + "event.timezone": "-02:00", + "event.type": [ + "end", + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.encrypted": "No", + "juniper.srx.process": "RT_FLOW", + "juniper.srx.reason": "Closed by junos-alg", + "juniper.srx.routing_instance": "default", + "juniper.srx.service_name": "junos-dns-udp", + "juniper.srx.session_id_32": "15361", + "juniper.srx.src_nat_rule_name": "our-nat-rule", + "juniper.srx.tag": "APPTRACK_SESSION_CLOSE", + "juniper.srx.uplink_rx_bytes": "0", + "juniper.srx.uplink_tx_bytes": "0", + "log.level": "informational", + "log.offset": 18627, + "network.bytes": 148, + "network.iana_number": "17", + "network.packets": 2, + "observer.egress.interface.name": "ge-0/0/0.0", + "observer.egress.zone": "untrust", + "observer.ingress.zone": "trust", + "observer.name": "SRX100HM", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "10.1.1.100", + "8.8.8.8", + "172.19.34.100", + "8.8.8.8" + ], + "rule.name": "default-permit", + "server.bytes": 82, + "server.ip": "8.8.8.8", + "server.nat.port": 53, + "server.packets": 1, + "server.port": 53, + "service.type": "juniper", + "source.bytes": 66, + "source.ip": "10.1.1.100", + "source.nat.ip": "172.19.34.100", + "source.nat.port": 26764, + "source.packets": 1, + "source.port": 63381, + "tags": [ + "juniper.srx", + "forwarded" + ] + } +] \ No newline at end of file diff --git a/x-pack/filebeat/module/juniper/srx/test/idp.log b/x-pack/filebeat/module/juniper/srx/test/idp.log new file mode 100644 index 00000000000..c05d9732fb5 --- /dev/null +++ b/x-pack/filebeat/module/juniper/srx/test/idp.log @@ -0,0 +1,7 @@ +<165>1 2020-03-02T23:13:03.193Z idp1 RT_IDP - IDP_ATTACK_LOG_EVENT [junos@2636.1.1.1.2.28 epoch-time="1583190783" message-type="SIG" source-address="10.11.11.1" source-port="12345" destination-address="187.188.188.10" destination-port="123" protocol-name="TCP" service-name="SERVICE_IDP" application-name="HTTP" rule-name="3" rulebase-name="IPS" policy-name="Recommended" export-id="20175" repeat-count="0" action="DROP" threat-severity="HIGH" attack-name="HTTP:MISC:GENERIC-DIR-TRAVERSAL" nat-source-address="0.0.0.0" nat-source-port="13312" nat-destination-address="3.3.10.11" nat-destination-port="9757" elapsed-time="0" inbound-bytes="0" outbound-bytes="0" inbound-packets="0" outbound-packets="0" source-zone-name="UNTRUST" source-interface-name="reth1.24" destination-zone-name="DMZ" destination-interface-name="reth2.21" packet-log-id="0" alert="no" username="unknown-user" roles="N/A" index="cnm" type="idp" message="-"] +<165>1 2020-03-02T23:13:03.197Z idp1 RT_IDP - IDP_ATTACK_LOG_EVENT [junos@2636.1.1.1.2.28 epoch-time="1583190783" message-type="SIG" source-address="10.11.11.1" source-port="12345" destination-address="187.188.188.10" destination-port="123" protocol-name="TCP" service-name="SERVICE_IDP" application-name="HTTP" rule-name="3" rulebase-name="IPS" policy-name="Recommended" export-id="20175" repeat-count="0" action="DROP" threat-severity="CRITICAL" attack-name="TCP:C2S:AMBIG:C2S-SYN-DATA" nat-source-address="0.0.0.0" nat-source-port="13312" nat-destination-address="3.3.10.11" nat-destination-port="9757" elapsed-time="0" inbound-bytes="0" outbound-bytes="0" inbound-packets="0" outbound-packets="0" source-zone-name="UNTRUST" source-interface-name="reth1.24" destination-zone-name="DMZ" destination-interface-name="reth2.21" packet-log-id="0" alert="no" username="unknown-user" roles="N/A" index="cnm" type="idp" message="-"] +<165>1 2007-02-15T09:17:15.719Z idp1 RT_IDP - IDP_ATTACK_LOG_EVENT [junos@2636.1.1.1.2.135 epoch-time="1507845354" message-type="SIG" source-address="183.78.180.27" source-port="45610" destination-address="118.127.111.1" destination-port="80" protocol-name="TCP" service-name="SERVICE_IDP" application-name="HTTP" rule-name="9" rulebase-name="IPS" policy-name="Recommended" export-id="15229" repeat-count="0" action="DROP" threat-severity="HIGH" attack-name="TROJAN:ZMEU-BOT-SCAN" nat-source-address="0.0.0.0" nat-source-port="0" nat-destination-address="172.19.13.11" nat-destination-port="0" elapsed-time="0" inbound-bytes="0" outbound-bytes="0" inbound-packets="0" outbound-packets="0" source-zone-name="sec-zone-name-internet" source-interface-name="reth0.11" destination-zone-name="dst-sec-zone1-outside" destination-interface-name="reth1.1" packet-log-id="0" alert="no" username="N/A" roles="N/A" message="-"] +<165>1 2017-10-13T08:55:55.792+11:00 idp1 RT_IDP - IDP_ATTACK_LOG_EVENT [junos@2636.1.1.1.2.135 epoch-time="1507845354" message-type="SIG" source-address="183.78.180.27" source-port="45610" destination-address="118.127.30.11" destination-port="80" protocol-name="TCP" service-name="SERVICE_IDP" application-name="HTTP" rule-name="9" rulebase-name="IPS" policy-name="Recommended" export-id="15229" repeat-count="0" action="DROP" threat-severity="HIGH" attack-name="TROJAN:ZMEU-BOT-SCAN" nat-source-address="0.0.0.0" nat-source-port="0" nat-destination-address="172.16.1.10" nat-destination-port="0" elapsed-time="0" inbound-bytes="0" outbound-bytes="0" inbound-packets="0" outbound-packets="0" source-zone-name="sec-zone-name-internet" source-interface-name="reth0.11" destination-zone-name="dst-sec-zone1-outside" destination-interface-name="reth1.1" packet-log-id="0" alert="no" username="N/A" roles="N/A" message="-"] +<165>1 2011-10-23T02:06:26.544 SRX34001 RT_IDP - IDP_APPDDOS_APP_STATE_EVENT [junos@2636.1.1.1.2.35 epoch-time="1319367986" ddos-application-name="Webserver" destination-zone-name="untrust" destination-interface-name="reth0.0" destination-address="172.27.14.203" destination-port="80" protocol-name="TCP" service-name="HTTP" rule-name="1" rulebase-name="DDOS" policy-name="A DoS-Webserver" repeat-count="0" message="Connection rate exceeded limit 60" context-value="N/A"] +<165>1 2011-10-23T16:28:31.696 SRX34001 RT_IDP - IDP_APPDDOS_APP_ATTACK_EVENT [junos@2636.1.1.1.2.35 epoch-time="1319419711" ddos-application-name="Webserver" source-zone-name="trust" source-interface-name="reth1.O" source-address="192.168.14.214" source-port="50825" destination-zone-name="untrust" destination-interface-name="reth0.0" destination-address="172.27.14.203" destination-port="80" protocol-name="TCP" service-name="HTTP" rule-name="1" ruleebase-name="DDOS" policy-name="AppDoS-Webserver" repeat-count="0" action="NONE" threat-severity="INFO" connection-hit-rate="30" context-name="http-get-url" context-hit-rate="123" context-value-hit-rate="0" time-scope="PEER" time-count="3" time-period="60" context-value="N/A"] +<165>1 2012-10-23T17:28:31.696 SRX34001 RT_IDP - IDP_APPDDOS_APP_ATTACK_EVENT_LS [junos@2636.1.1.1.2.35 epoch-time="1419419711" ddos-application-name="Webserver" source-zone-name="trust" source-interface-name="reth3.0" source-address="193.168.14.214" source-port="50825" destination-zone-name="untrust" destination-interface-name="reth0.1" destination-address="172.30.20.201" destination-port="80" protocol-name="TCP" service-name="HTTP" rule-name="1" ruleebase-name="DDOS02" policy-name="AppDoS-Webserver" repeat-count="0" action="NONE" threat-severity="INFO" connection-hit-rate="30" context-name="http-get-url" context-hit-rate="123" context-value-hit-rate="0" time-scope="PEER" time-count="3" time-period="60" context-value="N/A"] diff --git a/x-pack/filebeat/module/juniper/srx/test/idp.log-expected.json b/x-pack/filebeat/module/juniper/srx/test/idp.log-expected.json new file mode 100644 index 00000000000..7704c88fac0 --- /dev/null +++ b/x-pack/filebeat/module/juniper/srx/test/idp.log-expected.json @@ -0,0 +1,537 @@ +[ + { + "@timestamp": "2020-03-02T21:13:03.193-02:00", + "client.bytes": 0, + "client.ip": "10.11.11.1", + "client.nat.port": 13312, + "client.packets": 0, + "client.port": 12345, + "destination.bytes": 0, + "destination.ip": "187.188.188.10", + "destination.nat.ip": "3.3.10.11", + "destination.nat.port": 9757, + "destination.packets": 0, + "destination.port": 123, + "event.action": "security_threat", + "event.category": [ + "network", + "intrusion_detection" + ], + "event.dataset": "juniper.srx", + "event.duration": 0, + "event.end": "2020-03-02T21:13:03.193-02:00", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "epoch-time=\"1583190783\" message-type=\"SIG\" source-address=\"10.11.11.1\" source-port=\"12345\" destination-address=\"187.188.188.10\" destination-port=\"123\" protocol-name=\"TCP\" service-name=\"SERVICE_IDP\" application-name=\"HTTP\" rule-name=\"3\" rulebase-name=\"IPS\" policy-name=\"Recommended\" export-id=\"20175\" repeat-count=\"0\" action=\"DROP\" threat-severity=\"HIGH\" attack-name=\"HTTP:MISC:GENERIC-DIR-TRAVERSAL\" nat-source-address=\"0.0.0.0\" nat-source-port=\"13312\" nat-destination-address=\"3.3.10.11\" nat-destination-port=\"9757\" elapsed-time=\"0\" inbound-bytes=\"0\" outbound-bytes=\"0\" inbound-packets=\"0\" outbound-packets=\"0\" source-zone-name=\"UNTRUST\" source-interface-name=\"reth1.24\" destination-zone-name=\"DMZ\" destination-interface-name=\"reth2.21\" packet-log-id=\"0\" alert=\"no\" username=\"unknown-user\" roles=\"N/A\" index=\"cnm\" type=\"idp\" message=\"-\"", + "event.outcome": "success", + "event.severity": "165", + "event.start": "2020-03-02T21:13:03.193-02:00", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "DROP", + "juniper.srx.alert": "no", + "juniper.srx.application_name": "HTTP", + "juniper.srx.attack_name": "HTTP:MISC:GENERIC-DIR-TRAVERSAL", + "juniper.srx.epoch_time": "1583190783", + "juniper.srx.export_id": "20175", + "juniper.srx.index": "cnm", + "juniper.srx.message_type": "SIG", + "juniper.srx.packet_log_id": "0", + "juniper.srx.policy_name": "Recommended", + "juniper.srx.process": "RT_IDP", + "juniper.srx.repeat_count": "0", + "juniper.srx.service_name": "SERVICE_IDP", + "juniper.srx.tag": "IDP_ATTACK_LOG_EVENT", + "juniper.srx.threat_severity": "HIGH", + "juniper.srx.type": "idp", + "log.level": "notification", + "log.offset": 0, + "network.protocol": "TCP", + "observer.egress.interface.name": "reth2.21", + "observer.egress.zone": "DMZ", + "observer.ingress.interface.name": "reth1.24", + "observer.ingress.zone": "UNTRUST", + "observer.name": "idp1", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "10.11.11.1", + "187.188.188.10", + "0.0.0.0", + "3.3.10.11" + ], + "rule.id": "3", + "rule.name": "IPS", + "server.bytes": 0, + "server.ip": "187.188.188.10", + "server.nat.port": 9757, + "server.packets": 0, + "server.port": 123, + "service.type": "juniper", + "source.bytes": 0, + "source.ip": "10.11.11.1", + "source.nat.ip": "0.0.0.0", + "source.nat.port": 13312, + "source.packets": 0, + "source.port": 12345, + "source.user.name": "unknown-user", + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2020-03-02T21:13:03.197-02:00", + "client.bytes": 0, + "client.ip": "10.11.11.1", + "client.nat.port": 13312, + "client.packets": 0, + "client.port": 12345, + "destination.bytes": 0, + "destination.ip": "187.188.188.10", + "destination.nat.ip": "3.3.10.11", + "destination.nat.port": 9757, + "destination.packets": 0, + "destination.port": 123, + "event.action": "security_threat", + "event.category": [ + "network", + "intrusion_detection" + ], + "event.dataset": "juniper.srx", + "event.duration": 0, + "event.end": "2020-03-02T21:13:03.197-02:00", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "epoch-time=\"1583190783\" message-type=\"SIG\" source-address=\"10.11.11.1\" source-port=\"12345\" destination-address=\"187.188.188.10\" destination-port=\"123\" protocol-name=\"TCP\" service-name=\"SERVICE_IDP\" application-name=\"HTTP\" rule-name=\"3\" rulebase-name=\"IPS\" policy-name=\"Recommended\" export-id=\"20175\" repeat-count=\"0\" action=\"DROP\" threat-severity=\"CRITICAL\" attack-name=\"TCP:C2S:AMBIG:C2S-SYN-DATA\" nat-source-address=\"0.0.0.0\" nat-source-port=\"13312\" nat-destination-address=\"3.3.10.11\" nat-destination-port=\"9757\" elapsed-time=\"0\" inbound-bytes=\"0\" outbound-bytes=\"0\" inbound-packets=\"0\" outbound-packets=\"0\" source-zone-name=\"UNTRUST\" source-interface-name=\"reth1.24\" destination-zone-name=\"DMZ\" destination-interface-name=\"reth2.21\" packet-log-id=\"0\" alert=\"no\" username=\"unknown-user\" roles=\"N/A\" index=\"cnm\" type=\"idp\" message=\"-\"", + "event.outcome": "success", + "event.severity": "165", + "event.start": "2020-03-02T21:13:03.197-02:00", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "DROP", + "juniper.srx.alert": "no", + "juniper.srx.application_name": "HTTP", + "juniper.srx.attack_name": "TCP:C2S:AMBIG:C2S-SYN-DATA", + "juniper.srx.epoch_time": "1583190783", + "juniper.srx.export_id": "20175", + "juniper.srx.index": "cnm", + "juniper.srx.message_type": "SIG", + "juniper.srx.packet_log_id": "0", + "juniper.srx.policy_name": "Recommended", + "juniper.srx.process": "RT_IDP", + "juniper.srx.repeat_count": "0", + "juniper.srx.service_name": "SERVICE_IDP", + "juniper.srx.tag": "IDP_ATTACK_LOG_EVENT", + "juniper.srx.threat_severity": "CRITICAL", + "juniper.srx.type": "idp", + "log.level": "notification", + "log.offset": 929, + "network.protocol": "TCP", + "observer.egress.interface.name": "reth2.21", + "observer.egress.zone": "DMZ", + "observer.ingress.interface.name": "reth1.24", + "observer.ingress.zone": "UNTRUST", + "observer.name": "idp1", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "10.11.11.1", + "187.188.188.10", + "0.0.0.0", + "3.3.10.11" + ], + "rule.id": "3", + "rule.name": "IPS", + "server.bytes": 0, + "server.ip": "187.188.188.10", + "server.nat.port": 9757, + "server.packets": 0, + "server.port": 123, + "service.type": "juniper", + "source.bytes": 0, + "source.ip": "10.11.11.1", + "source.nat.ip": "0.0.0.0", + "source.nat.port": 13312, + "source.packets": 0, + "source.port": 12345, + "source.user.name": "unknown-user", + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2007-02-15T07:17:15.719-02:00", + "client.bytes": 0, + "client.ip": "183.78.180.27", + "client.nat.port": 0, + "client.packets": 0, + "client.port": 45610, + "destination.bytes": 0, + "destination.ip": "118.127.111.1", + "destination.nat.ip": "172.19.13.11", + "destination.nat.port": 0, + "destination.packets": 0, + "destination.port": 80, + "event.action": "security_threat", + "event.category": [ + "network", + "intrusion_detection" + ], + "event.dataset": "juniper.srx", + "event.duration": 0, + "event.end": "2007-02-15T07:17:15.719-02:00", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "epoch-time=\"1507845354\" message-type=\"SIG\" source-address=\"183.78.180.27\" source-port=\"45610\" destination-address=\"118.127.111.1\" destination-port=\"80\" protocol-name=\"TCP\" service-name=\"SERVICE_IDP\" application-name=\"HTTP\" rule-name=\"9\" rulebase-name=\"IPS\" policy-name=\"Recommended\" export-id=\"15229\" repeat-count=\"0\" action=\"DROP\" threat-severity=\"HIGH\" attack-name=\"TROJAN:ZMEU-BOT-SCAN\" nat-source-address=\"0.0.0.0\" nat-source-port=\"0\" nat-destination-address=\"172.19.13.11\" nat-destination-port=\"0\" elapsed-time=\"0\" inbound-bytes=\"0\" outbound-bytes=\"0\" inbound-packets=\"0\" outbound-packets=\"0\" source-zone-name=\"sec-zone-name-internet\" source-interface-name=\"reth0.11\" destination-zone-name=\"dst-sec-zone1-outside\" destination-interface-name=\"reth1.1\" packet-log-id=\"0\" alert=\"no\" username=\"N/A\" roles=\"N/A\" message=\"-\"", + "event.outcome": "success", + "event.severity": "165", + "event.start": "2007-02-15T07:17:15.719-02:00", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "DROP", + "juniper.srx.alert": "no", + "juniper.srx.application_name": "HTTP", + "juniper.srx.attack_name": "TROJAN:ZMEU-BOT-SCAN", + "juniper.srx.epoch_time": "1507845354", + "juniper.srx.export_id": "15229", + "juniper.srx.message_type": "SIG", + "juniper.srx.packet_log_id": "0", + "juniper.srx.policy_name": "Recommended", + "juniper.srx.process": "RT_IDP", + "juniper.srx.repeat_count": "0", + "juniper.srx.service_name": "SERVICE_IDP", + "juniper.srx.tag": "IDP_ATTACK_LOG_EVENT", + "juniper.srx.threat_severity": "HIGH", + "log.level": "notification", + "log.offset": 1857, + "network.protocol": "TCP", + "observer.egress.interface.name": "reth1.1", + "observer.egress.zone": "dst-sec-zone1-outside", + "observer.ingress.interface.name": "reth0.11", + "observer.ingress.zone": "sec-zone-name-internet", + "observer.name": "idp1", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "183.78.180.27", + "118.127.111.1", + "0.0.0.0", + "172.19.13.11" + ], + "rule.id": "9", + "rule.name": "IPS", + "server.bytes": 0, + "server.ip": "118.127.111.1", + "server.nat.port": 0, + "server.packets": 0, + "server.port": 80, + "service.type": "juniper", + "source.bytes": 0, + "source.ip": "183.78.180.27", + "source.nat.ip": "0.0.0.0", + "source.nat.port": 0, + "source.packets": 0, + "source.port": 45610, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2017-10-12T19:55:55.792-02:00", + "client.bytes": 0, + "client.ip": "183.78.180.27", + "client.nat.port": 0, + "client.packets": 0, + "client.port": 45610, + "destination.bytes": 0, + "destination.ip": "118.127.30.11", + "destination.nat.ip": "172.16.1.10", + "destination.nat.port": 0, + "destination.packets": 0, + "destination.port": 80, + "event.action": "security_threat", + "event.category": [ + "network", + "intrusion_detection" + ], + "event.dataset": "juniper.srx", + "event.duration": 0, + "event.end": "2017-10-12T19:55:55.792-02:00", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "epoch-time=\"1507845354\" message-type=\"SIG\" source-address=\"183.78.180.27\" source-port=\"45610\" destination-address=\"118.127.30.11\" destination-port=\"80\" protocol-name=\"TCP\" service-name=\"SERVICE_IDP\" application-name=\"HTTP\" rule-name=\"9\" rulebase-name=\"IPS\" policy-name=\"Recommended\" export-id=\"15229\" repeat-count=\"0\" action=\"DROP\" threat-severity=\"HIGH\" attack-name=\"TROJAN:ZMEU-BOT-SCAN\" nat-source-address=\"0.0.0.0\" nat-source-port=\"0\" nat-destination-address=\"172.16.1.10\" nat-destination-port=\"0\" elapsed-time=\"0\" inbound-bytes=\"0\" outbound-bytes=\"0\" inbound-packets=\"0\" outbound-packets=\"0\" source-zone-name=\"sec-zone-name-internet\" source-interface-name=\"reth0.11\" destination-zone-name=\"dst-sec-zone1-outside\" destination-interface-name=\"reth1.1\" packet-log-id=\"0\" alert=\"no\" username=\"N/A\" roles=\"N/A\" message=\"-\"", + "event.outcome": "success", + "event.severity": "165", + "event.start": "2017-10-12T19:55:55.792-02:00", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "DROP", + "juniper.srx.alert": "no", + "juniper.srx.application_name": "HTTP", + "juniper.srx.attack_name": "TROJAN:ZMEU-BOT-SCAN", + "juniper.srx.epoch_time": "1507845354", + "juniper.srx.export_id": "15229", + "juniper.srx.message_type": "SIG", + "juniper.srx.packet_log_id": "0", + "juniper.srx.policy_name": "Recommended", + "juniper.srx.process": "RT_IDP", + "juniper.srx.repeat_count": "0", + "juniper.srx.service_name": "SERVICE_IDP", + "juniper.srx.tag": "IDP_ATTACK_LOG_EVENT", + "juniper.srx.threat_severity": "HIGH", + "log.level": "notification", + "log.offset": 2773, + "network.protocol": "TCP", + "observer.egress.interface.name": "reth1.1", + "observer.egress.zone": "dst-sec-zone1-outside", + "observer.ingress.interface.name": "reth0.11", + "observer.ingress.zone": "sec-zone-name-internet", + "observer.name": "idp1", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "183.78.180.27", + "118.127.30.11", + "0.0.0.0", + "172.16.1.10" + ], + "rule.id": "9", + "rule.name": "IPS", + "server.bytes": 0, + "server.ip": "118.127.30.11", + "server.nat.port": 0, + "server.packets": 0, + "server.port": 80, + "service.type": "juniper", + "source.bytes": 0, + "source.ip": "183.78.180.27", + "source.nat.ip": "0.0.0.0", + "source.nat.port": 0, + "source.packets": 0, + "source.port": 45610, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2011-10-23T02:06:26.544-02:00", + "destination.ip": "172.27.14.203", + "destination.port": 80, + "event.action": "application_ddos", + "event.category": [ + "network", + "intrusion_detection" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "epoch-time=\"1319367986\" ddos-application-name=\"Webserver\" destination-zone-name=\"untrust\" destination-interface-name=\"reth0.0\" destination-address=\"172.27.14.203\" destination-port=\"80\" protocol-name=\"TCP\" service-name=\"HTTP\" rule-name=\"1\" rulebase-name=\"DDOS\" policy-name=\"A DoS-Webserver\" repeat-count=\"0\" message=\"Connection rate exceeded limit 60\" context-value=\"N/A\"", + "event.outcome": "success", + "event.severity": "165", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.ddos_application_name": "Webserver", + "juniper.srx.epoch_time": "1319367986", + "juniper.srx.policy_name": "A DoS-Webserver", + "juniper.srx.process": "RT_IDP", + "juniper.srx.repeat_count": "0", + "juniper.srx.service_name": "HTTP", + "juniper.srx.tag": "IDP_APPDDOS_APP_STATE_EVENT", + "log.level": "notification", + "log.offset": 3693, + "message": "Connection rate exceeded limit 60", + "network.protocol": "TCP", + "observer.egress.interface.name": "reth0.0", + "observer.egress.zone": "untrust", + "observer.name": "SRX34001", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "172.27.14.203" + ], + "rule.id": "1", + "rule.name": "DDOS", + "server.ip": "172.27.14.203", + "server.port": 80, + "service.type": "juniper", + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2011-10-23T16:28:31.696-02:00", + "client.ip": "192.168.14.214", + "client.port": 50825, + "destination.ip": "172.27.14.203", + "destination.port": 80, + "event.action": "application_ddos", + "event.category": [ + "network", + "intrusion_detection" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "epoch-time=\"1319419711\" ddos-application-name=\"Webserver\" source-zone-name=\"trust\" source-interface-name=\"reth1.O\" source-address=\"192.168.14.214\" source-port=\"50825\" destination-zone-name=\"untrust\" destination-interface-name=\"reth0.0\" destination-address=\"172.27.14.203\" destination-port=\"80\" protocol-name=\"TCP\" service-name=\"HTTP\" rule-name=\"1\" ruleebase-name=\"DDOS\" policy-name=\"AppDoS-Webserver\" repeat-count=\"0\" action=\"NONE\" threat-severity=\"INFO\" connection-hit-rate=\"30\" context-name=\"http-get-url\" context-hit-rate=\"123\" context-value-hit-rate=\"0\" time-scope=\"PEER\" time-count=\"3\" time-period=\"60\" context-value=\"N/A\"", + "event.outcome": "success", + "event.severity": "165", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "NONE", + "juniper.srx.connection_hit_rate": "30", + "juniper.srx.context_hit_rate": "123", + "juniper.srx.context_name": "http-get-url", + "juniper.srx.context_value_hit_rate": "0", + "juniper.srx.ddos_application_name": "Webserver", + "juniper.srx.epoch_time": "1319419711", + "juniper.srx.policy_name": "AppDoS-Webserver", + "juniper.srx.process": "RT_IDP", + "juniper.srx.repeat_count": "0", + "juniper.srx.ruleebase_name": "DDOS", + "juniper.srx.service_name": "HTTP", + "juniper.srx.tag": "IDP_APPDDOS_APP_ATTACK_EVENT", + "juniper.srx.threat_severity": "INFO", + "juniper.srx.time_count": "3", + "juniper.srx.time_period": "60", + "juniper.srx.time_scope": "PEER", + "log.level": "notification", + "log.offset": 4165, + "network.protocol": "TCP", + "observer.egress.interface.name": "reth0.0", + "observer.egress.zone": "untrust", + "observer.ingress.interface.name": "reth1.O", + "observer.ingress.zone": "trust", + "observer.name": "SRX34001", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "192.168.14.214", + "172.27.14.203" + ], + "rule.id": "1", + "server.ip": "172.27.14.203", + "server.port": 80, + "service.type": "juniper", + "source.ip": "192.168.14.214", + "source.port": 50825, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2012-10-23T17:28:31.696-02:00", + "client.ip": "193.168.14.214", + "client.port": 50825, + "destination.ip": "172.30.20.201", + "destination.port": 80, + "event.action": "application_ddos", + "event.category": [ + "network", + "intrusion_detection" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "epoch-time=\"1419419711\" ddos-application-name=\"Webserver\" source-zone-name=\"trust\" source-interface-name=\"reth3.0\" source-address=\"193.168.14.214\" source-port=\"50825\" destination-zone-name=\"untrust\" destination-interface-name=\"reth0.1\" destination-address=\"172.30.20.201\" destination-port=\"80\" protocol-name=\"TCP\" service-name=\"HTTP\" rule-name=\"1\" ruleebase-name=\"DDOS02\" policy-name=\"AppDoS-Webserver\" repeat-count=\"0\" action=\"NONE\" threat-severity=\"INFO\" connection-hit-rate=\"30\" context-name=\"http-get-url\" context-hit-rate=\"123\" context-value-hit-rate=\"0\" time-scope=\"PEER\" time-count=\"3\" time-period=\"60\" context-value=\"N/A\"", + "event.outcome": "success", + "event.severity": "165", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "NONE", + "juniper.srx.connection_hit_rate": "30", + "juniper.srx.context_hit_rate": "123", + "juniper.srx.context_name": "http-get-url", + "juniper.srx.context_value_hit_rate": "0", + "juniper.srx.ddos_application_name": "Webserver", + "juniper.srx.epoch_time": "1419419711", + "juniper.srx.policy_name": "AppDoS-Webserver", + "juniper.srx.process": "RT_IDP", + "juniper.srx.repeat_count": "0", + "juniper.srx.ruleebase_name": "DDOS02", + "juniper.srx.service_name": "HTTP", + "juniper.srx.tag": "IDP_APPDDOS_APP_ATTACK_EVENT_LS", + "juniper.srx.threat_severity": "INFO", + "juniper.srx.time_count": "3", + "juniper.srx.time_period": "60", + "juniper.srx.time_scope": "PEER", + "log.level": "notification", + "log.offset": 4895, + "network.protocol": "TCP", + "observer.egress.interface.name": "reth0.1", + "observer.egress.zone": "untrust", + "observer.ingress.interface.name": "reth3.0", + "observer.ingress.zone": "trust", + "observer.name": "SRX34001", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "193.168.14.214", + "172.30.20.201" + ], + "rule.id": "1", + "server.ip": "172.30.20.201", + "server.port": 80, + "service.type": "juniper", + "source.ip": "193.168.14.214", + "source.port": 50825, + "tags": [ + "juniper.srx", + "forwarded" + ] + } +] \ No newline at end of file diff --git a/x-pack/filebeat/module/juniper/srx/test/ids.log b/x-pack/filebeat/module/juniper/srx/test/ids.log new file mode 100644 index 00000000000..5b87817da86 --- /dev/null +++ b/x-pack/filebeat/module/juniper/srx/test/ids.log @@ -0,0 +1,12 @@ +<11>1 2018-07-19T18:17:02.309-05:00 rtr199 RT_IDS - RT_SCREEN_TCP [junos@2636.1.1.1.2.137 attack-name="TCP sweep!" source-address="113.113.17.17" source-port="6000" destination-address="40.177.177.1" destination-port="1433" source-zone-name="untrust" interface-name="fe-0/0/2.0" action="drop"] +<11>1 2018-07-19T18:18:02.309-05:00 rtr199 RT_IDS - RT_SCREEN_TCP [junos@2636.1.1.1.2.36 attack-name="WinNuke attack!" source-address="2000:0000:0000:0000:0000:0000:0000:0002" source-port="3240" destination-address="2001:0000:0000:0000:0000:0000:0000:0002" destination-port="139" source-zone-name="untrust" interface-name="fe-0/0/2.0" action="drop"] +<11>1 2018-07-19T18:19:02.309-05:00 rtr199 RT_IDS - RT_SCREEN_TCP [junos@2636.1.1.1.2.40 attack-name="SYN flood!" source-address="1.1.1.2" source-port="40001" destination-address="2.2.2.2" destination-port="50010" source-zone-name="trustZone" interface-name="ge-0/0/1.0" action="drop"] +<11>1 2018-07-19T18:22:02.309-05:00 rtr199 RT_IDS - RT_SCREEN_UDP [junos@2636.1.1.1.2.40 attack-name="UDP flood!" source-address="111.1.1.3" source-port="40001" destination-address="3.4.2.2" destination-port="53" source-zone-name="trustZone" interface-name="ge-0/0/1.0" action="drop"] +<11>1 2018-07-19T18:25:02.309-05:00 rtr199 RT_IDS - RT_SCREEN_ICMP [junos@2636.1.1.1.2.40 attack-name="ICMP fragment!" source-address="111.1.1.3" destination-address="3.4.2.2" source-zone-name="trustZone" interface-name="ge-0/0/1.0" action="drop"] +<11>1 2018-07-19T18:26:02.309-05:00 rtr199 RT_IDS - RT_SCREEN_IP [junos@2636.1.1.1.2.40 attack-name="Record Route IP option!" source-address="111.1.1.3" destination-address="3.4.2.2" protocol-id="1" source-zone-name="trustZone" interface-name="ge-0/0/1.0" action="drop"] +<11>1 2018-07-19T18:27:02.309-05:00 rtr199 RT_IDS - RT_SCREEN_IP [junos@2636.1.1.1.2.40 attack-name="Tunnel GRE 6in6!" source-address="1212::12" destination-address="1111::11" protocol-id="1" source-zone-name="trustZone" interface-name="ge-0/0/1.0" action="drop"] +<11>1 2018-07-19T18:28:02.309-05:00 rtr199 RT_IDS - RT_SCREEN_IP [junos@2636.1.1.1.2.40 attack-name="Tunnel GRE 4in4!" source-address="12.12.12.1" destination-address="11.11.11.1" protocol-id="1" source-zone-name="trustZone" interface-name="ge-0/0/1.0" action="drop"] +<11>1 2018-07-19T19:19:02.309-05:00 rtr199 RT_IDS - RT_SCREEN_TCP_DST_IP [junos@2636.1.1.1.2.40 attack-name="SYN flood!" destination-address="2.2.2.2" source-zone-name="trustZone" interface-name="ge-0/0/1.0" action="alarm-without-drop"] +<11>1 2018-07-19T19:19:02.309-05:00 rtr199 RT_IDS - RT_SCREEN_TCP_SRC_IP [junos@2636.1.1.1.2.40 attack-name="SYN flood!" source-address="111.1.1.3" source-zone-name="trustZone" interface-name="ge-0/0/1.0" action="alarm-without-drop"] +<11>1 2020-07-17T09:54:43.912+02:00 rtr199 RT_IDS - RT_SCREEN_TCP [junos@2636.1.1.1.2.129 attack-name="TCP port scan!" source-address="10.1.1.100" source-port="50630" destination-address="10.1.1.1" destination-port="10778" source-zone-name="trust" interface-name="ge-0/0/1.0" action="drop"] +<11>1 2020-07-17T10:01:43.006+02:00 rtr199 RT_IDS - RT_SCREEN_TCP [junos@2636.1.1.1.2.129 attack-name="FIN but no ACK bit!" source-address="10.1.1.100" source-port="42799" destination-address="10.1.1.1" destination-port="7" source-zone-name="trust" interface-name="ge-0/0/1.0" action="drop"] diff --git a/x-pack/filebeat/module/juniper/srx/test/ids.log-expected.json b/x-pack/filebeat/module/juniper/srx/test/ids.log-expected.json new file mode 100644 index 00000000000..10abae2fa6d --- /dev/null +++ b/x-pack/filebeat/module/juniper/srx/test/ids.log-expected.json @@ -0,0 +1,699 @@ +[ + { + "@timestamp": "2018-07-19T21:17:02.309-02:00", + "client.ip": "113.113.17.17", + "client.port": 6000, + "destination.as.number": 4249, + "destination.as.organization.name": "Eli Lilly and Company", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.country_name": "United States", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, + "destination.ip": "40.177.177.1", + "destination.port": 1433, + "event.action": "sweep_detected", + "event.category": [ + "network", + "intrusion_detection" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "attack-name=\"TCP sweep!\" source-address=\"113.113.17.17\" source-port=\"6000\" destination-address=\"40.177.177.1\" destination-port=\"1433\" source-zone-name=\"untrust\" interface-name=\"fe-0/0/2.0\" action=\"drop\"", + "event.outcome": "success", + "event.severity": "11", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "drop", + "juniper.srx.attack_name": "TCP sweep!", + "juniper.srx.process": "RT_IDS", + "juniper.srx.tag": "RT_SCREEN_TCP", + "log.level": "error", + "log.offset": 0, + "observer.ingress.interface.name": "fe-0/0/2.0", + "observer.ingress.zone": "untrust", + "observer.name": "rtr199", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "113.113.17.17", + "40.177.177.1" + ], + "server.ip": "40.177.177.1", + "server.port": 1433, + "service.type": "juniper", + "source.as.number": 4134, + "source.as.organization.name": "No.31,Jin-rong Street", + "source.geo.continent_name": "Asia", + "source.geo.country_iso_code": "CN", + "source.geo.country_name": "China", + "source.geo.location.lat": 23.1167, + "source.geo.location.lon": 113.25, + "source.geo.region_iso_code": "CN-GD", + "source.geo.region_name": "Guangdong", + "source.ip": "113.113.17.17", + "source.port": 6000, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2018-07-19T21:18:02.309-02:00", + "client.ip": "2000:0000:0000:0000:0000:0000:0000:0002", + "client.port": 3240, + "destination.ip": "2001:0000:0000:0000:0000:0000:0000:0002", + "destination.port": 139, + "event.action": "attack_detected", + "event.category": [ + "network", + "intrusion_detection" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "attack-name=\"WinNuke attack!\" source-address=\"2000:0000:0000:0000:0000:0000:0000:0002\" source-port=\"3240\" destination-address=\"2001:0000:0000:0000:0000:0000:0000:0002\" destination-port=\"139\" source-zone-name=\"untrust\" interface-name=\"fe-0/0/2.0\" action=\"drop\"", + "event.outcome": "success", + "event.severity": "11", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "drop", + "juniper.srx.attack_name": "WinNuke attack!", + "juniper.srx.process": "RT_IDS", + "juniper.srx.tag": "RT_SCREEN_TCP", + "log.level": "error", + "log.offset": 294, + "observer.ingress.interface.name": "fe-0/0/2.0", + "observer.ingress.zone": "untrust", + "observer.name": "rtr199", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "2000:0000:0000:0000:0000:0000:0000:0002", + "2001:0000:0000:0000:0000:0000:0000:0002" + ], + "server.ip": "2001:0000:0000:0000:0000:0000:0000:0002", + "server.port": 139, + "service.type": "juniper", + "source.ip": "2000:0000:0000:0000:0000:0000:0000:0002", + "source.port": 3240, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2018-07-19T21:19:02.309-02:00", + "client.ip": "1.1.1.2", + "client.port": 40001, + "destination.as.number": 3215, + "destination.as.organization.name": "Orange", + "destination.geo.continent_name": "Europe", + "destination.geo.country_iso_code": "FR", + "destination.geo.country_name": "France", + "destination.geo.location.lat": 48.8582, + "destination.geo.location.lon": 2.3387, + "destination.ip": "2.2.2.2", + "destination.port": 50010, + "event.action": "flood_detected", + "event.category": [ + "network", + "intrusion_detection" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "attack-name=\"SYN flood!\" source-address=\"1.1.1.2\" source-port=\"40001\" destination-address=\"2.2.2.2\" destination-port=\"50010\" source-zone-name=\"trustZone\" interface-name=\"ge-0/0/1.0\" action=\"drop\"", + "event.outcome": "success", + "event.severity": "11", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "drop", + "juniper.srx.attack_name": "SYN flood!", + "juniper.srx.process": "RT_IDS", + "juniper.srx.tag": "RT_SCREEN_TCP", + "log.level": "error", + "log.offset": 644, + "observer.ingress.interface.name": "ge-0/0/1.0", + "observer.ingress.zone": "trustZone", + "observer.name": "rtr199", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "1.1.1.2", + "2.2.2.2" + ], + "server.ip": "2.2.2.2", + "server.port": 50010, + "service.type": "juniper", + "source.as.number": 13335, + "source.as.organization.name": "Cloudflare, Inc.", + "source.geo.continent_name": "Oceania", + "source.geo.country_iso_code": "AU", + "source.geo.country_name": "Australia", + "source.geo.location.lat": -33.494, + "source.geo.location.lon": 143.2104, + "source.ip": "1.1.1.2", + "source.port": 40001, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2018-07-19T21:22:02.309-02:00", + "client.ip": "111.1.1.3", + "client.port": 40001, + "destination.geo.city_name": "Seattle", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.country_name": "United States", + "destination.geo.location.lat": 47.6348, + "destination.geo.location.lon": -122.3451, + "destination.geo.region_iso_code": "US-WA", + "destination.geo.region_name": "Washington", + "destination.ip": "3.4.2.2", + "destination.port": 53, + "event.action": "flood_detected", + "event.category": [ + "network", + "intrusion_detection" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "attack-name=\"UDP flood!\" source-address=\"111.1.1.3\" source-port=\"40001\" destination-address=\"3.4.2.2\" destination-port=\"53\" source-zone-name=\"trustZone\" interface-name=\"ge-0/0/1.0\" action=\"drop\"", + "event.outcome": "success", + "event.severity": "11", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "drop", + "juniper.srx.attack_name": "UDP flood!", + "juniper.srx.process": "RT_IDS", + "juniper.srx.tag": "RT_SCREEN_UDP", + "log.level": "error", + "log.offset": 930, + "observer.ingress.interface.name": "ge-0/0/1.0", + "observer.ingress.zone": "trustZone", + "observer.name": "rtr199", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "111.1.1.3", + "3.4.2.2" + ], + "server.ip": "3.4.2.2", + "server.port": 53, + "service.type": "juniper", + "source.as.number": 56041, + "source.as.organization.name": "China Mobile communications corporation", + "source.geo.city_name": "Wenzhou", + "source.geo.continent_name": "Asia", + "source.geo.country_iso_code": "CN", + "source.geo.country_name": "China", + "source.geo.location.lat": 27.9983, + "source.geo.location.lon": 120.6666, + "source.geo.region_iso_code": "CN-ZJ", + "source.geo.region_name": "Zhejiang", + "source.ip": "111.1.1.3", + "source.port": 40001, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2018-07-19T21:25:02.309-02:00", + "client.ip": "111.1.1.3", + "destination.geo.city_name": "Seattle", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.country_name": "United States", + "destination.geo.location.lat": 47.6348, + "destination.geo.location.lon": -122.3451, + "destination.geo.region_iso_code": "US-WA", + "destination.geo.region_name": "Washington", + "destination.ip": "3.4.2.2", + "event.action": "fragment_detected", + "event.category": [ + "network", + "intrusion_detection" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "attack-name=\"ICMP fragment!\" source-address=\"111.1.1.3\" destination-address=\"3.4.2.2\" source-zone-name=\"trustZone\" interface-name=\"ge-0/0/1.0\" action=\"drop\"", + "event.outcome": "success", + "event.severity": "11", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "drop", + "juniper.srx.attack_name": "ICMP fragment!", + "juniper.srx.process": "RT_IDS", + "juniper.srx.tag": "RT_SCREEN_ICMP", + "log.level": "error", + "log.offset": 1215, + "observer.ingress.interface.name": "ge-0/0/1.0", + "observer.ingress.zone": "trustZone", + "observer.name": "rtr199", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "111.1.1.3", + "3.4.2.2" + ], + "server.ip": "3.4.2.2", + "service.type": "juniper", + "source.as.number": 56041, + "source.as.organization.name": "China Mobile communications corporation", + "source.geo.city_name": "Wenzhou", + "source.geo.continent_name": "Asia", + "source.geo.country_iso_code": "CN", + "source.geo.country_name": "China", + "source.geo.location.lat": 27.9983, + "source.geo.location.lon": 120.6666, + "source.geo.region_iso_code": "CN-ZJ", + "source.geo.region_name": "Zhejiang", + "source.ip": "111.1.1.3", + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2018-07-19T21:26:02.309-02:00", + "client.ip": "111.1.1.3", + "destination.geo.city_name": "Seattle", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.country_name": "United States", + "destination.geo.location.lat": 47.6348, + "destination.geo.location.lon": -122.3451, + "destination.geo.region_iso_code": "US-WA", + "destination.geo.region_name": "Washington", + "destination.ip": "3.4.2.2", + "event.category": [ + "network", + "intrusion_detection" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "attack-name=\"Record Route IP option!\" source-address=\"111.1.1.3\" destination-address=\"3.4.2.2\" protocol-id=\"1\" source-zone-name=\"trustZone\" interface-name=\"ge-0/0/1.0\" action=\"drop\"", + "event.outcome": "success", + "event.severity": "11", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "drop", + "juniper.srx.attack_name": "Record Route IP option!", + "juniper.srx.process": "RT_IDS", + "juniper.srx.tag": "RT_SCREEN_IP", + "log.level": "error", + "log.offset": 1463, + "network.iana_number": "1", + "observer.ingress.interface.name": "ge-0/0/1.0", + "observer.ingress.zone": "trustZone", + "observer.name": "rtr199", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "111.1.1.3", + "3.4.2.2" + ], + "server.ip": "3.4.2.2", + "service.type": "juniper", + "source.as.number": 56041, + "source.as.organization.name": "China Mobile communications corporation", + "source.geo.city_name": "Wenzhou", + "source.geo.continent_name": "Asia", + "source.geo.country_iso_code": "CN", + "source.geo.country_name": "China", + "source.geo.location.lat": 27.9983, + "source.geo.location.lon": 120.6666, + "source.geo.region_iso_code": "CN-ZJ", + "source.geo.region_name": "Zhejiang", + "source.ip": "111.1.1.3", + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2018-07-19T21:27:02.309-02:00", + "client.ip": "1212::12", + "destination.ip": "1111::11", + "event.action": "tunneling_screen", + "event.category": [ + "network", + "intrusion_detection" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "attack-name=\"Tunnel GRE 6in6!\" source-address=\"1212::12\" destination-address=\"1111::11\" protocol-id=\"1\" source-zone-name=\"trustZone\" interface-name=\"ge-0/0/1.0\" action=\"drop\"", + "event.outcome": "success", + "event.severity": "11", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "drop", + "juniper.srx.attack_name": "Tunnel GRE 6in6!", + "juniper.srx.process": "RT_IDS", + "juniper.srx.tag": "RT_SCREEN_IP", + "log.level": "error", + "log.offset": 1734, + "network.iana_number": "1", + "observer.ingress.interface.name": "ge-0/0/1.0", + "observer.ingress.zone": "trustZone", + "observer.name": "rtr199", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "1212::12", + "1111::11" + ], + "server.ip": "1111::11", + "service.type": "juniper", + "source.ip": "1212::12", + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2018-07-19T21:28:02.309-02:00", + "client.ip": "12.12.12.1", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.country_name": "United States", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, + "destination.ip": "11.11.11.1", + "event.action": "tunneling_screen", + "event.category": [ + "network", + "intrusion_detection" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "attack-name=\"Tunnel GRE 4in4!\" source-address=\"12.12.12.1\" destination-address=\"11.11.11.1\" protocol-id=\"1\" source-zone-name=\"trustZone\" interface-name=\"ge-0/0/1.0\" action=\"drop\"", + "event.outcome": "success", + "event.severity": "11", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "drop", + "juniper.srx.attack_name": "Tunnel GRE 4in4!", + "juniper.srx.process": "RT_IDS", + "juniper.srx.tag": "RT_SCREEN_IP", + "log.level": "error", + "log.offset": 1998, + "network.iana_number": "1", + "observer.ingress.interface.name": "ge-0/0/1.0", + "observer.ingress.zone": "trustZone", + "observer.name": "rtr199", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "12.12.12.1", + "11.11.11.1" + ], + "server.ip": "11.11.11.1", + "service.type": "juniper", + "source.as.number": 32328, + "source.as.organization.name": "Alascom, Inc.", + "source.geo.continent_name": "North America", + "source.geo.country_iso_code": "US", + "source.geo.country_name": "United States", + "source.geo.location.lat": 37.751, + "source.geo.location.lon": -97.822, + "source.ip": "12.12.12.1", + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2018-07-19T22:19:02.309-02:00", + "destination.as.number": 3215, + "destination.as.organization.name": "Orange", + "destination.geo.continent_name": "Europe", + "destination.geo.country_iso_code": "FR", + "destination.geo.country_name": "France", + "destination.geo.location.lat": 48.8582, + "destination.geo.location.lon": 2.3387, + "destination.ip": "2.2.2.2", + "event.action": "flood_detected", + "event.category": [ + "network", + "intrusion_detection" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "attack-name=\"SYN flood!\" destination-address=\"2.2.2.2\" source-zone-name=\"trustZone\" interface-name=\"ge-0/0/1.0\" action=\"alarm-without-drop\"", + "event.outcome": "success", + "event.severity": "11", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "alarm-without-drop", + "juniper.srx.attack_name": "SYN flood!", + "juniper.srx.process": "RT_IDS", + "juniper.srx.tag": "RT_SCREEN_TCP_DST_IP", + "log.level": "error", + "log.offset": 2266, + "observer.ingress.interface.name": "ge-0/0/1.0", + "observer.ingress.zone": "trustZone", + "observer.name": "rtr199", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "2.2.2.2" + ], + "server.ip": "2.2.2.2", + "service.type": "juniper", + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2018-07-19T22:19:02.309-02:00", + "client.ip": "111.1.1.3", + "event.action": "flood_detected", + "event.category": [ + "network", + "intrusion_detection" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "attack-name=\"SYN flood!\" source-address=\"111.1.1.3\" source-zone-name=\"trustZone\" interface-name=\"ge-0/0/1.0\" action=\"alarm-without-drop\"", + "event.outcome": "success", + "event.severity": "11", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "alarm-without-drop", + "juniper.srx.attack_name": "SYN flood!", + "juniper.srx.process": "RT_IDS", + "juniper.srx.tag": "RT_SCREEN_TCP_SRC_IP", + "log.level": "error", + "log.offset": 2503, + "observer.ingress.interface.name": "ge-0/0/1.0", + "observer.ingress.zone": "trustZone", + "observer.name": "rtr199", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "111.1.1.3" + ], + "service.type": "juniper", + "source.as.number": 56041, + "source.as.organization.name": "China Mobile communications corporation", + "source.geo.city_name": "Wenzhou", + "source.geo.continent_name": "Asia", + "source.geo.country_iso_code": "CN", + "source.geo.country_name": "China", + "source.geo.location.lat": 27.9983, + "source.geo.location.lon": 120.6666, + "source.geo.region_iso_code": "CN-ZJ", + "source.geo.region_name": "Zhejiang", + "source.ip": "111.1.1.3", + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2020-07-17T05:54:43.912-02:00", + "client.ip": "10.1.1.100", + "client.port": 50630, + "destination.ip": "10.1.1.1", + "destination.port": 10778, + "event.action": "scan_detected", + "event.category": [ + "network", + "intrusion_detection" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "attack-name=\"TCP port scan!\" source-address=\"10.1.1.100\" source-port=\"50630\" destination-address=\"10.1.1.1\" destination-port=\"10778\" source-zone-name=\"trust\" interface-name=\"ge-0/0/1.0\" action=\"drop\"", + "event.outcome": "success", + "event.severity": "11", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "drop", + "juniper.srx.attack_name": "TCP port scan!", + "juniper.srx.process": "RT_IDS", + "juniper.srx.tag": "RT_SCREEN_TCP", + "log.level": "error", + "log.offset": 2737, + "observer.ingress.interface.name": "ge-0/0/1.0", + "observer.ingress.zone": "trust", + "observer.name": "rtr199", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "10.1.1.100", + "10.1.1.1" + ], + "server.ip": "10.1.1.1", + "server.port": 10778, + "service.type": "juniper", + "source.ip": "10.1.1.100", + "source.port": 50630, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2020-07-17T06:01:43.006-02:00", + "client.ip": "10.1.1.100", + "client.port": 42799, + "destination.ip": "10.1.1.1", + "destination.port": 7, + "event.action": "illegal_tcp_flag_detected", + "event.category": [ + "network", + "intrusion_detection" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "attack-name=\"FIN but no ACK bit!\" source-address=\"10.1.1.100\" source-port=\"42799\" destination-address=\"10.1.1.1\" destination-port=\"7\" source-zone-name=\"trust\" interface-name=\"ge-0/0/1.0\" action=\"drop\"", + "event.outcome": "success", + "event.severity": "11", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "drop", + "juniper.srx.attack_name": "FIN but no ACK bit!", + "juniper.srx.process": "RT_IDS", + "juniper.srx.tag": "RT_SCREEN_TCP", + "log.level": "error", + "log.offset": 3028, + "observer.ingress.interface.name": "ge-0/0/1.0", + "observer.ingress.zone": "trust", + "observer.name": "rtr199", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "10.1.1.100", + "10.1.1.1" + ], + "server.ip": "10.1.1.1", + "server.port": 7, + "service.type": "juniper", + "source.ip": "10.1.1.100", + "source.port": 42799, + "tags": [ + "juniper.srx", + "forwarded" + ] + } +] \ No newline at end of file diff --git a/x-pack/filebeat/module/juniper/srx/test/secintel.log b/x-pack/filebeat/module/juniper/srx/test/secintel.log new file mode 100644 index 00000000000..12f8f137c7f --- /dev/null +++ b/x-pack/filebeat/module/juniper/srx/test/secintel.log @@ -0,0 +1,2 @@ +<14>1 2016-10-17T15:18:11.618Z SRX-1500 RT_SECINTEL - SECINTEL_ACTION_LOG [junos@2636.1.1.1.2.129 category="secintel" sub-category="Blacklist" action="BLOCK" action-detail="DROP" http-host="N/A" threat-severity="0" source-address="5.196.121.161" source-port="1" destination-address="10.10.0.10" destination-port="24039" protocol-id="1" application="N/A" nested-application="N/A" feed-name="Tor_Exit_Nodes" policy-name="cc_policy" profile-name="Blacklist" username="N/A" roles="N/A" session-id-32="572564" source-zone-name="Outside" destination-zone-name="DMZ"] +<14>1 2016-10-17T15:18:11.618Z SRX-1500 RT_SECINTEL - SECINTEL_ACTION_LOG [junos@2636.1.1.1.2.129 category="secintel" sub-category="CC" action="BLOCK" action-detail="CLOSE REDIRECT MSG" http-host="dummy_host" threat-severity="10" source-address="1.1.1.1" source-port="36612" destination-address="10.0.0.1" destination-port="80" protocol-id="6" application="HTTP" nested-application="N/A" feed-name="cc_url_data" policy-name="test" profile-name="test-profile" username="N/A" roles="N/A" session-id-32="502362" source-zone-name="Inside" destination-zone-name="Outside" occur-count="0"] diff --git a/x-pack/filebeat/module/juniper/srx/test/secintel.log-expected.json b/x-pack/filebeat/module/juniper/srx/test/secintel.log-expected.json new file mode 100644 index 00000000000..49667e85897 --- /dev/null +++ b/x-pack/filebeat/module/juniper/srx/test/secintel.log-expected.json @@ -0,0 +1,140 @@ +[ + { + "@timestamp": "2016-10-17T13:18:11.618-02:00", + "client.ip": "5.196.121.161", + "client.port": 1, + "destination.ip": "10.10.0.10", + "destination.port": 24039, + "event.action": "malware_detected", + "event.category": [ + "network", + "malware" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "category=\"secintel\" sub-category=\"Blacklist\" action=\"BLOCK\" action-detail=\"DROP\" http-host=\"N/A\" threat-severity=\"0\" source-address=\"5.196.121.161\" source-port=\"1\" destination-address=\"10.10.0.10\" destination-port=\"24039\" protocol-id=\"1\" application=\"N/A\" nested-application=\"N/A\" feed-name=\"Tor_Exit_Nodes\" policy-name=\"cc_policy\" profile-name=\"Blacklist\" username=\"N/A\" roles=\"N/A\" session-id-32=\"572564\" source-zone-name=\"Outside\" destination-zone-name=\"DMZ\"", + "event.outcome": "success", + "event.severity": "14", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "BLOCK", + "juniper.srx.action_detail": "DROP", + "juniper.srx.category": "secintel", + "juniper.srx.feed_name": "Tor_Exit_Nodes", + "juniper.srx.policy_name": "cc_policy", + "juniper.srx.process": "RT_SECINTEL", + "juniper.srx.profile_name": "Blacklist", + "juniper.srx.session_id_32": "572564", + "juniper.srx.sub_category": "Blacklist", + "juniper.srx.tag": "SECINTEL_ACTION_LOG", + "juniper.srx.threat_severity": "0", + "log.level": "informational", + "log.offset": 0, + "network.iana_number": "1", + "observer.egress.zone": "DMZ", + "observer.ingress.zone": "Outside", + "observer.name": "SRX-1500", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "5.196.121.161", + "10.10.0.10" + ], + "server.ip": "10.10.0.10", + "server.port": 24039, + "service.type": "juniper", + "source.as.number": 16276, + "source.as.organization.name": "OVH SAS", + "source.geo.continent_name": "Europe", + "source.geo.country_iso_code": "FR", + "source.geo.country_name": "France", + "source.geo.location.lat": 48.8582, + "source.geo.location.lon": 2.3387, + "source.ip": "5.196.121.161", + "source.port": 1, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2016-10-17T13:18:11.618-02:00", + "client.ip": "1.1.1.1", + "client.port": 36612, + "destination.ip": "10.0.0.1", + "destination.port": 80, + "event.action": "malware_detected", + "event.category": [ + "network", + "malware" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "category=\"secintel\" sub-category=\"CC\" action=\"BLOCK\" action-detail=\"CLOSE REDIRECT MSG\" http-host=\"dummy_host\" threat-severity=\"10\" source-address=\"1.1.1.1\" source-port=\"36612\" destination-address=\"10.0.0.1\" destination-port=\"80\" protocol-id=\"6\" application=\"HTTP\" nested-application=\"N/A\" feed-name=\"cc_url_data\" policy-name=\"test\" profile-name=\"test-profile\" username=\"N/A\" roles=\"N/A\" session-id-32=\"502362\" source-zone-name=\"Inside\" destination-zone-name=\"Outside\" occur-count=\"0\"", + "event.outcome": "success", + "event.severity": "14", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "BLOCK", + "juniper.srx.action_detail": "CLOSE REDIRECT MSG", + "juniper.srx.application": "HTTP", + "juniper.srx.category": "secintel", + "juniper.srx.feed_name": "cc_url_data", + "juniper.srx.occur_count": "0", + "juniper.srx.policy_name": "test", + "juniper.srx.process": "RT_SECINTEL", + "juniper.srx.profile_name": "test-profile", + "juniper.srx.session_id_32": "502362", + "juniper.srx.sub_category": "CC", + "juniper.srx.tag": "SECINTEL_ACTION_LOG", + "juniper.srx.threat_severity": "10", + "log.level": "informational", + "log.offset": 561, + "network.iana_number": "6", + "observer.egress.zone": "Outside", + "observer.ingress.zone": "Inside", + "observer.name": "SRX-1500", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.hosts": [ + "dummy_host" + ], + "related.ip": [ + "1.1.1.1", + "10.0.0.1" + ], + "server.ip": "10.0.0.1", + "server.port": 80, + "service.type": "juniper", + "source.as.number": 13335, + "source.as.organization.name": "Cloudflare, Inc.", + "source.geo.continent_name": "Oceania", + "source.geo.country_iso_code": "AU", + "source.geo.country_name": "Australia", + "source.geo.location.lat": -33.494, + "source.geo.location.lon": 143.2104, + "source.ip": "1.1.1.1", + "source.port": 36612, + "tags": [ + "juniper.srx", + "forwarded" + ], + "url.domain": "dummy_host" + } +] \ No newline at end of file diff --git a/x-pack/filebeat/module/juniper/srx/test/utm.log b/x-pack/filebeat/module/juniper/srx/test/utm.log new file mode 100644 index 00000000000..61c320ae885 --- /dev/null +++ b/x-pack/filebeat/module/juniper/srx/test/utm.log @@ -0,0 +1,12 @@ +<12>1 2016-02-18T01:32:50.391Z utm-srx550-b RT_UTM - WEBFILTER_URL_BLOCKED [junos@2636.1.1.1.2.86 source-address="192.168.1.100" source-port="58071" destination-address="103.235.46.39" destination-port="80" category="cat1" reason="BY_BLACK_LIST" profile="uf1" url="www.baidu.com" obj="/" username="user01" roles="N/A"] +<12>1 2016-02-18T01:32:50.391Z utm-srx550-b RT_UTM - WEBFILTER_URL_PERMITTED [junos@2636.1.1.1.2.86 source-address="10.10.10.50" source-port="1402" destination-address="216.200.241.66" destination-port="80" category="N/A" reason="BY_OTHER" profile="wf-profile" url="www.checkpoint.com" obj="/css/homepage2012.css" username="user02" roles="N/A"] +<12>1 2010-02-08T08:29:28.565Z SRX650-1 RT_UTM - AV_VIRUS_DETECTED_MT [junos@2636.1.1.1.2.40 source-address="188.40.238.250" source-port="80" destination-address="10.1.1.103" destination-port="47095" source-zone-name="untrust" filename="www.eicar.org/download/eicar.com" temporary-filename="www.eicar.org/download/eicar.com" name="EICAR-Test-File" url="EICAR-Test-File"] +<12>1 2010-02-08T08:29:28.565Z SRX650-1 RT_UTM - AV_SCANNER_DROP_FILE_MT [junos@2636.1.1.1.2.40 source-address="74.125.155.147" source-port="80" destination-address="10.1.1.103" destination-port="33578" filename="www.google.com/" error-code="14" error-message="scan engine is not ready"] +<12>1 2010-01-29T10:59:59.660Z SRX650-1 RT_UTM - AV_HUGE_FILE_DROPPED_MT [junos@2636.1.1.1.2.40 source-address="10.2.1.101" source-port="80" destination-address="10.1.1.103" destination-port="51727" filename="10.2.1.101/images/junos- srxsme-10.2-20100106.0-domestic.tgz"] +<14>1 2016-02-18T01:33:50.391Z utm-srx550-b RT_UTM - ANTISPAM_SPAM_DETECTED_MT [junos@2636.1.1.1.2.86 source-zone="trust" destination-zone="untrust" source-name="N/A" source-address="10.10.10.1" profile-name="antispam01" action="drop" reason="Match local blacklist" username="user01" roles="N/A"] +<14>1 2016-02-18T01:34:50.391Z utm-srx550-b RT_UTM - CONTENT_FILTERING_BLOCKED_MT [junos@2636.1.1.1.2.86 source-zone="untrust" destination-zone="trust" protocol="http" source-address="192.0.2.3" source-port="58071" destination-address="198.51.100.2" destination-port="80" profile-name="content02" action="drop" reason="blocked due to file extension block list" username="user01@testuser.com" roles="N/A" filename="test.cmd"] +<12>1 2016-02-19T01:32:50.391Z utm-srx550-b RT_UTM - WEBFILTER_URL_BLOCKED_LS [junos@2636.1.1.1.2.86 source-address="192.168.1.100" source-port="58071" destination-address="103.235.46.39" destination-port="80" category="cat1" reason="BY_BLACK_LIST" profile="uf1" url="www.baidu.com" obj="/" username="user01" roles="N/A"] +<12>1 2011-02-08T08:29:28.565Z SRX650-1 RT_UTM - AV_VIRUS_DETECTED_MT_LS [junos@2636.1.1.1.2.40 source-address="188.40.238.250" source-port="80" destination-address="10.1.1.103" destination-port="47095" source-zone-name="untrust" filename="www.eicar.org/download/eicar.com" temporary-filename="www.eicar.org/download/eicar.com" name="EICAR-Test-File" url="EICAR-Test-File"] +<14>1 2020-07-14T14:16:18.345Z SRX650-1 RT_UTM - WEBFILTER_URL_PERMITTED [junos@2636.1.1.1.2.129 source-zone="trust" destination-zone="untrust" source-address="10.1.1.100" source-port="58974" destination-address="104.26.15.142" destination-port="443" session-id="16297" application="UNKNOWN" nested-application="UNKNOWN" category="Enhanced_Information_Technology" reason="BY_SITE_REPUTATION_MODERATELY_SAFE" profile="WCF1" url="datawrapper.dwcdn.net" obj="/" username="N/A" roles="N/A" application-sub-category="N/A" urlcategory-risk="0"] +<12>1 2020-07-14T14:16:29.541Z SRX650-1 RT_UTM - WEBFILTER_URL_BLOCKED [junos@2636.1.1.1.2.129 source-zone="trust" destination-zone="untrust" source-address="10.1.1.100" source-port="59075" destination-address="85.114.159.93" destination-port="443" session-id="16490" application="UNKNOWN" nested-application="UNKNOWN" category="Enhanced_Advertisements" reason="BY_SITE_REPUTATION_SUSPICIOUS" profile="WCF1" url="dsp.adfarm1.adition.com" obj="/" username="N/A" roles="N/A" application-sub-category="N/A" urlcategory-risk="3"] +<12>1 2020-07-14T14:17:04.733Z SRX650-1 RT_UTM - AV_FILE_NOT_SCANNED_DROPPED_MT [junos@2636.1.1.1.2.129 source-zone="trust" destination-zone="untrust" source-address="23.209.86.45" source-port="80" destination-address="10.1.1.100" destination-port="58954" profile-name="Custom-Sophos-Profile" filename="download.cdn.mozilla.net/pub/firefox/releases/78.0.2/update/win64/de/firefox-78.0.2.complete.mar" action="BLOCKED" reason="exceeding maximum content size" error-code="7" username="N/A" roles="N/A"] diff --git a/x-pack/filebeat/module/juniper/srx/test/utm.log-expected.json b/x-pack/filebeat/module/juniper/srx/test/utm.log-expected.json new file mode 100644 index 00000000000..f9890a6ca0f --- /dev/null +++ b/x-pack/filebeat/module/juniper/srx/test/utm.log-expected.json @@ -0,0 +1,698 @@ +[ + { + "@timestamp": "2016-02-17T23:32:50.391-02:00", + "client.ip": "192.168.1.100", + "client.port": 58071, + "destination.as.number": 55967, + "destination.as.organization.name": "Beijing Baidu Netcom Science and Technology Co., Ltd.", + "destination.geo.continent_name": "Asia", + "destination.geo.country_iso_code": "HK", + "destination.geo.country_name": "Hong Kong", + "destination.geo.location.lat": 22.25, + "destination.geo.location.lon": 114.1667, + "destination.ip": "103.235.46.39", + "destination.port": 80, + "event.action": "web_filter", + "event.category": [ + "network", + "malware" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "source-address=\"192.168.1.100\" source-port=\"58071\" destination-address=\"103.235.46.39\" destination-port=\"80\" category=\"cat1\" reason=\"BY_BLACK_LIST\" profile=\"uf1\" url=\"www.baidu.com\" obj=\"/\" username=\"user01\" roles=\"N/A\"", + "event.outcome": "success", + "event.severity": "12", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.category": "cat1", + "juniper.srx.process": "RT_UTM", + "juniper.srx.profile": "uf1", + "juniper.srx.reason": "BY_BLACK_LIST", + "juniper.srx.tag": "WEBFILTER_URL_BLOCKED", + "log.level": "warning", + "log.offset": 0, + "observer.name": "utm-srx550-b", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.hosts": [ + "www.baidu.com" + ], + "related.ip": [ + "192.168.1.100", + "103.235.46.39" + ], + "server.ip": "103.235.46.39", + "server.port": 80, + "service.type": "juniper", + "source.ip": "192.168.1.100", + "source.port": 58071, + "source.user.name": "user01", + "tags": [ + "juniper.srx", + "forwarded" + ], + "url.domain": "www.baidu.com", + "url.path": "/" + }, + { + "@timestamp": "2016-02-17T23:32:50.391-02:00", + "client.ip": "10.10.10.50", + "client.port": 1402, + "destination.as.number": 6461, + "destination.as.organization.name": "Zayo Bandwidth", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.country_name": "United States", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, + "destination.ip": "216.200.241.66", + "destination.port": 80, + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.kind": "event", + "event.module": "juniper", + "event.original": "source-address=\"10.10.10.50\" source-port=\"1402\" destination-address=\"216.200.241.66\" destination-port=\"80\" category=\"N/A\" reason=\"BY_OTHER\" profile=\"wf-profile\" url=\"www.checkpoint.com\" obj=\"/css/homepage2012.css\" username=\"user02\" roles=\"N/A\"", + "event.outcome": "success", + "event.severity": "12", + "event.timezone": "-02:00", + "event.type": [ + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.process": "RT_UTM", + "juniper.srx.profile": "wf-profile", + "juniper.srx.reason": "BY_OTHER", + "juniper.srx.tag": "WEBFILTER_URL_PERMITTED", + "log.level": "warning", + "log.offset": 319, + "observer.name": "utm-srx550-b", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.hosts": [ + "www.checkpoint.com" + ], + "related.ip": [ + "10.10.10.50", + "216.200.241.66" + ], + "server.ip": "216.200.241.66", + "server.port": 80, + "service.type": "juniper", + "source.ip": "10.10.10.50", + "source.port": 1402, + "source.user.name": "user02", + "tags": [ + "juniper.srx", + "forwarded" + ], + "url.domain": "www.checkpoint.com", + "url.path": "/css/homepage2012.css" + }, + { + "@timestamp": "2010-02-08T06:29:28.565-02:00", + "client.ip": "188.40.238.250", + "client.port": 80, + "destination.ip": "10.1.1.103", + "destination.port": 47095, + "event.action": "virus_detected", + "event.category": [ + "network", + "malware" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "source-address=\"188.40.238.250\" source-port=\"80\" destination-address=\"10.1.1.103\" destination-port=\"47095\" source-zone-name=\"untrust\" filename=\"www.eicar.org/download/eicar.com\" temporary-filename=\"www.eicar.org/download/eicar.com\" name=\"EICAR-Test-File\" url=\"EICAR-Test-File\"", + "event.outcome": "success", + "event.severity": "12", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "file.name": "www.eicar.org/download/eicar.com", + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.name": "EICAR-Test-File", + "juniper.srx.process": "RT_UTM", + "juniper.srx.tag": "AV_VIRUS_DETECTED_MT", + "juniper.srx.temporary_filename": "www.eicar.org/download/eicar.com", + "log.level": "warning", + "log.offset": 664, + "observer.ingress.zone": "untrust", + "observer.name": "SRX650-1", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.hosts": [ + "EICAR-Test-File" + ], + "related.ip": [ + "188.40.238.250", + "10.1.1.103" + ], + "server.ip": "10.1.1.103", + "server.port": 47095, + "service.type": "juniper", + "source.as.number": 24940, + "source.as.organization.name": "Hetzner Online GmbH", + "source.geo.continent_name": "Europe", + "source.geo.country_iso_code": "DE", + "source.geo.country_name": "Germany", + "source.geo.location.lat": 51.2993, + "source.geo.location.lon": 9.491, + "source.ip": "188.40.238.250", + "source.port": 80, + "tags": [ + "juniper.srx", + "forwarded" + ], + "url.domain": "EICAR-Test-File" + }, + { + "@timestamp": "2010-02-08T06:29:28.565-02:00", + "client.ip": "74.125.155.147", + "client.port": 80, + "destination.ip": "10.1.1.103", + "destination.port": 33578, + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.kind": "event", + "event.module": "juniper", + "event.original": "source-address=\"74.125.155.147\" source-port=\"80\" destination-address=\"10.1.1.103\" destination-port=\"33578\" filename=\"www.google.com/\" error-code=\"14\" error-message=\"scan engine is not ready\"", + "event.outcome": "success", + "event.severity": "12", + "event.timezone": "-02:00", + "event.type": [ + "allowed", + "connection" + ], + "file.name": "www.google.com/", + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.error_code": "14", + "juniper.srx.error_message": "scan engine is not ready", + "juniper.srx.process": "RT_UTM", + "juniper.srx.tag": "AV_SCANNER_DROP_FILE_MT", + "log.level": "warning", + "log.offset": 1035, + "observer.name": "SRX650-1", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "74.125.155.147", + "10.1.1.103" + ], + "server.ip": "10.1.1.103", + "server.port": 33578, + "service.type": "juniper", + "source.as.number": 15169, + "source.as.organization.name": "Google LLC", + "source.geo.continent_name": "North America", + "source.geo.country_iso_code": "US", + "source.geo.country_name": "United States", + "source.geo.location.lat": 37.751, + "source.geo.location.lon": -97.822, + "source.ip": "74.125.155.147", + "source.port": 80, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2010-01-29T08:59:59.660-02:00", + "client.ip": "10.2.1.101", + "client.port": 80, + "destination.ip": "10.1.1.103", + "destination.port": 51727, + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.kind": "event", + "event.module": "juniper", + "event.original": "source-address=\"10.2.1.101\" source-port=\"80\" destination-address=\"10.1.1.103\" destination-port=\"51727\" filename=\"10.2.1.101/images/junos- srxsme-10.2-20100106.0-domestic.tgz\"", + "event.outcome": "success", + "event.severity": "12", + "event.timezone": "-02:00", + "event.type": [ + "allowed", + "connection" + ], + "file.name": "10.2.1.101/images/junos- srxsme-10.2-20100106.0-domestic.tgz", + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.process": "RT_UTM", + "juniper.srx.tag": "AV_HUGE_FILE_DROPPED_MT", + "log.level": "warning", + "log.offset": 1323, + "observer.name": "SRX650-1", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "10.2.1.101", + "10.1.1.103" + ], + "server.ip": "10.1.1.103", + "server.port": 51727, + "service.type": "juniper", + "source.ip": "10.2.1.101", + "source.port": 80, + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2016-02-17T23:33:50.391-02:00", + "client.ip": "10.10.10.1", + "event.action": "antispam_filter", + "event.category": [ + "network", + "malware" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "source-zone=\"trust\" destination-zone=\"untrust\" source-name=\"N/A\" source-address=\"10.10.10.1\" profile-name=\"antispam01\" action=\"drop\" reason=\"Match local blacklist\" username=\"user01\" roles=\"N/A\"", + "event.outcome": "success", + "event.severity": "14", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "drop", + "juniper.srx.process": "RT_UTM", + "juniper.srx.profile_name": "antispam01", + "juniper.srx.reason": "Match local blacklist", + "juniper.srx.tag": "ANTISPAM_SPAM_DETECTED_MT", + "log.level": "informational", + "log.offset": 1595, + "observer.egress.zone": "untrust", + "observer.ingress.zone": "trust", + "observer.name": "utm-srx550-b", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "10.10.10.1" + ], + "service.type": "juniper", + "source.ip": "10.10.10.1", + "source.user.name": "user01", + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2016-02-17T23:34:50.391-02:00", + "client.ip": "192.0.2.3", + "client.port": 58071, + "destination.ip": "198.51.100.2", + "destination.port": 80, + "event.action": "content_filter", + "event.category": [ + "network", + "malware" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "source-zone=\"untrust\" destination-zone=\"trust\" protocol=\"http\" source-address=\"192.0.2.3\" source-port=\"58071\" destination-address=\"198.51.100.2\" destination-port=\"80\" profile-name=\"content02\" action=\"drop\" reason=\"blocked due to file extension block list\" username=\"user01@testuser.com\" roles=\"N/A\" filename=\"test.cmd\"", + "event.outcome": "success", + "event.severity": "14", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "file.name": "test.cmd", + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "drop", + "juniper.srx.process": "RT_UTM", + "juniper.srx.profile_name": "content02", + "juniper.srx.reason": "blocked due to file extension block list", + "juniper.srx.tag": "CONTENT_FILTERING_BLOCKED_MT", + "log.level": "informational", + "log.offset": 1892, + "network.protocol": "http", + "observer.egress.zone": "trust", + "observer.ingress.zone": "untrust", + "observer.name": "utm-srx550-b", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "192.0.2.3", + "198.51.100.2" + ], + "server.ip": "198.51.100.2", + "server.port": 80, + "service.type": "juniper", + "source.ip": "192.0.2.3", + "source.port": 58071, + "source.user.name": "user01@testuser.com", + "tags": [ + "juniper.srx", + "forwarded" + ] + }, + { + "@timestamp": "2016-02-18T23:32:50.391-02:00", + "client.ip": "192.168.1.100", + "client.port": 58071, + "destination.as.number": 55967, + "destination.as.organization.name": "Beijing Baidu Netcom Science and Technology Co., Ltd.", + "destination.geo.continent_name": "Asia", + "destination.geo.country_iso_code": "HK", + "destination.geo.country_name": "Hong Kong", + "destination.geo.location.lat": 22.25, + "destination.geo.location.lon": 114.1667, + "destination.ip": "103.235.46.39", + "destination.port": 80, + "event.action": "web_filter", + "event.category": [ + "network", + "malware" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "source-address=\"192.168.1.100\" source-port=\"58071\" destination-address=\"103.235.46.39\" destination-port=\"80\" category=\"cat1\" reason=\"BY_BLACK_LIST\" profile=\"uf1\" url=\"www.baidu.com\" obj=\"/\" username=\"user01\" roles=\"N/A\"", + "event.outcome": "success", + "event.severity": "12", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.category": "cat1", + "juniper.srx.process": "RT_UTM", + "juniper.srx.profile": "uf1", + "juniper.srx.reason": "BY_BLACK_LIST", + "juniper.srx.tag": "WEBFILTER_URL_BLOCKED_LS", + "log.level": "warning", + "log.offset": 2317, + "observer.name": "utm-srx550-b", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.hosts": [ + "www.baidu.com" + ], + "related.ip": [ + "192.168.1.100", + "103.235.46.39" + ], + "server.ip": "103.235.46.39", + "server.port": 80, + "service.type": "juniper", + "source.ip": "192.168.1.100", + "source.port": 58071, + "source.user.name": "user01", + "tags": [ + "juniper.srx", + "forwarded" + ], + "url.domain": "www.baidu.com", + "url.path": "/" + }, + { + "@timestamp": "2011-02-08T06:29:28.565-02:00", + "client.ip": "188.40.238.250", + "client.port": 80, + "destination.ip": "10.1.1.103", + "destination.port": 47095, + "event.action": "virus_detected", + "event.category": [ + "network", + "malware" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "source-address=\"188.40.238.250\" source-port=\"80\" destination-address=\"10.1.1.103\" destination-port=\"47095\" source-zone-name=\"untrust\" filename=\"www.eicar.org/download/eicar.com\" temporary-filename=\"www.eicar.org/download/eicar.com\" name=\"EICAR-Test-File\" url=\"EICAR-Test-File\"", + "event.outcome": "success", + "event.severity": "12", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "file.name": "www.eicar.org/download/eicar.com", + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.name": "EICAR-Test-File", + "juniper.srx.process": "RT_UTM", + "juniper.srx.tag": "AV_VIRUS_DETECTED_MT_LS", + "juniper.srx.temporary_filename": "www.eicar.org/download/eicar.com", + "log.level": "warning", + "log.offset": 2639, + "observer.ingress.zone": "untrust", + "observer.name": "SRX650-1", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.hosts": [ + "EICAR-Test-File" + ], + "related.ip": [ + "188.40.238.250", + "10.1.1.103" + ], + "server.ip": "10.1.1.103", + "server.port": 47095, + "service.type": "juniper", + "source.as.number": 24940, + "source.as.organization.name": "Hetzner Online GmbH", + "source.geo.continent_name": "Europe", + "source.geo.country_iso_code": "DE", + "source.geo.country_name": "Germany", + "source.geo.location.lat": 51.2993, + "source.geo.location.lon": 9.491, + "source.ip": "188.40.238.250", + "source.port": 80, + "tags": [ + "juniper.srx", + "forwarded" + ], + "url.domain": "EICAR-Test-File" + }, + { + "@timestamp": "2020-07-14T12:16:18.345-02:00", + "client.ip": "10.1.1.100", + "client.port": 58974, + "destination.as.number": 13335, + "destination.as.organization.name": "Cloudflare, Inc.", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.country_name": "United States", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, + "destination.ip": "104.26.15.142", + "destination.port": 443, + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.kind": "event", + "event.module": "juniper", + "event.original": "source-zone=\"trust\" destination-zone=\"untrust\" source-address=\"10.1.1.100\" source-port=\"58974\" destination-address=\"104.26.15.142\" destination-port=\"443\" session-id=\"16297\" application=\"UNKNOWN\" nested-application=\"UNKNOWN\" category=\"Enhanced_Information_Technology\" reason=\"BY_SITE_REPUTATION_MODERATELY_SAFE\" profile=\"WCF1\" url=\"datawrapper.dwcdn.net\" obj=\"/\" username=\"N/A\" roles=\"N/A\" application-sub-category=\"N/A\" urlcategory-risk=\"0\"", + "event.outcome": "success", + "event.risk_score": "0", + "event.severity": "14", + "event.timezone": "-02:00", + "event.type": [ + "allowed", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.category": "Enhanced_Information_Technology", + "juniper.srx.process": "RT_UTM", + "juniper.srx.profile": "WCF1", + "juniper.srx.reason": "BY_SITE_REPUTATION_MODERATELY_SAFE", + "juniper.srx.session_id": "16297", + "juniper.srx.tag": "WEBFILTER_URL_PERMITTED", + "log.level": "informational", + "log.offset": 3013, + "observer.egress.zone": "untrust", + "observer.ingress.zone": "trust", + "observer.name": "SRX650-1", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.hosts": [ + "datawrapper.dwcdn.net" + ], + "related.ip": [ + "10.1.1.100", + "104.26.15.142" + ], + "server.ip": "104.26.15.142", + "server.port": 443, + "service.type": "juniper", + "source.ip": "10.1.1.100", + "source.port": 58974, + "tags": [ + "juniper.srx", + "forwarded" + ], + "url.domain": "datawrapper.dwcdn.net", + "url.path": "/" + }, + { + "@timestamp": "2020-07-14T12:16:29.541-02:00", + "client.ip": "10.1.1.100", + "client.port": 59075, + "destination.as.number": 24961, + "destination.as.organization.name": "myLoc managed IT AG", + "destination.geo.continent_name": "Europe", + "destination.geo.country_iso_code": "DE", + "destination.geo.country_name": "Germany", + "destination.geo.location.lat": 51.2993, + "destination.geo.location.lon": 9.491, + "destination.ip": "85.114.159.93", + "destination.port": 443, + "event.action": "web_filter", + "event.category": [ + "network", + "malware" + ], + "event.dataset": "juniper.srx", + "event.kind": "alert", + "event.module": "juniper", + "event.original": "source-zone=\"trust\" destination-zone=\"untrust\" source-address=\"10.1.1.100\" source-port=\"59075\" destination-address=\"85.114.159.93\" destination-port=\"443\" session-id=\"16490\" application=\"UNKNOWN\" nested-application=\"UNKNOWN\" category=\"Enhanced_Advertisements\" reason=\"BY_SITE_REPUTATION_SUSPICIOUS\" profile=\"WCF1\" url=\"dsp.adfarm1.adition.com\" obj=\"/\" username=\"N/A\" roles=\"N/A\" application-sub-category=\"N/A\" urlcategory-risk=\"3\"", + "event.outcome": "success", + "event.risk_score": "3", + "event.severity": "12", + "event.timezone": "-02:00", + "event.type": [ + "info", + "denied", + "connection" + ], + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.category": "Enhanced_Advertisements", + "juniper.srx.process": "RT_UTM", + "juniper.srx.profile": "WCF1", + "juniper.srx.reason": "BY_SITE_REPUTATION_SUSPICIOUS", + "juniper.srx.session_id": "16490", + "juniper.srx.tag": "WEBFILTER_URL_BLOCKED", + "log.level": "warning", + "log.offset": 3552, + "observer.egress.zone": "untrust", + "observer.ingress.zone": "trust", + "observer.name": "SRX650-1", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.hosts": [ + "dsp.adfarm1.adition.com" + ], + "related.ip": [ + "10.1.1.100", + "85.114.159.93" + ], + "server.ip": "85.114.159.93", + "server.port": 443, + "service.type": "juniper", + "source.ip": "10.1.1.100", + "source.port": 59075, + "tags": [ + "juniper.srx", + "forwarded" + ], + "url.domain": "dsp.adfarm1.adition.com", + "url.path": "/" + }, + { + "@timestamp": "2020-07-14T12:17:04.733-02:00", + "client.ip": "23.209.86.45", + "client.port": 80, + "destination.ip": "10.1.1.100", + "destination.port": 58954, + "event.category": [ + "network" + ], + "event.dataset": "juniper.srx", + "event.kind": "event", + "event.module": "juniper", + "event.original": "source-zone=\"trust\" destination-zone=\"untrust\" source-address=\"23.209.86.45\" source-port=\"80\" destination-address=\"10.1.1.100\" destination-port=\"58954\" profile-name=\"Custom-Sophos-Profile\" filename=\"download.cdn.mozilla.net/pub/firefox/releases/78.0.2/update/win64/de/firefox-78.0.2.complete.mar\" action=\"BLOCKED\" reason=\"exceeding maximum content size\" error-code=\"7\" username=\"N/A\" roles=\"N/A\"", + "event.outcome": "success", + "event.severity": "12", + "event.timezone": "-02:00", + "event.type": [ + "allowed", + "connection" + ], + "file.name": "download.cdn.mozilla.net/pub/firefox/releases/78.0.2/update/win64/de/firefox-78.0.2.complete.mar", + "fileset.name": "srx", + "input.type": "log", + "juniper.srx.action": "BLOCKED", + "juniper.srx.error_code": "7", + "juniper.srx.process": "RT_UTM", + "juniper.srx.profile_name": "Custom-Sophos-Profile", + "juniper.srx.reason": "exceeding maximum content size", + "juniper.srx.tag": "AV_FILE_NOT_SCANNED_DROPPED_MT", + "log.level": "warning", + "log.offset": 4078, + "observer.egress.zone": "untrust", + "observer.ingress.zone": "trust", + "observer.name": "SRX650-1", + "observer.product": "SRX", + "observer.type": "firewall", + "observer.vendor": "Juniper", + "related.ip": [ + "23.209.86.45", + "10.1.1.100" + ], + "server.ip": "10.1.1.100", + "server.port": 58954, + "service.type": "juniper", + "source.as.number": 16625, + "source.as.organization.name": "Akamai Technologies, Inc.", + "source.geo.continent_name": "Europe", + "source.geo.country_iso_code": "NL", + "source.geo.country_name": "Netherlands", + "source.geo.location.lat": 52.3824, + "source.geo.location.lon": 4.8995, + "source.ip": "23.209.86.45", + "source.port": 80, + "tags": [ + "juniper.srx", + "forwarded" + ] + } +] \ No newline at end of file diff --git a/x-pack/filebeat/modules.d/juniper.yml.disabled b/x-pack/filebeat/modules.d/juniper.yml.disabled index e3359756d90..6ffe87834a4 100644 --- a/x-pack/filebeat/modules.d/juniper.yml.disabled +++ b/x-pack/filebeat/modules.d/juniper.yml.disabled @@ -39,3 +39,16 @@ # "local" (default) for system timezone. # "+02:00" for GMT+02:00 # var.tz_offset: local + + srx: + enabled: true + + # Set which input to use between tcp, udp (default) or file. + #var.input: udp + + # The interface to listen to syslog traffic. Defaults to + # localhost. Set to 0.0.0.0 to bind to all available interfaces. + #var.syslog_host: localhost + + # The port to listen for syslog traffic. Defaults to 9006. + #var.syslog_port: 9006 From 4dd8061938e8aee474ef6e378fe9aae2bd4837da Mon Sep 17 00:00:00 2001 From: Marius Iversen Date: Tue, 6 Oct 2020 11:06:39 +0200 Subject: [PATCH 29/93] [Beats][pytest] Asserting if filebeat logs include errors (#20999) First iteration on tackling this issue, allowing to assert on errors in filebeat, since any test will fail afterwards anyway, and the logs should not include errors. This could be anything from Elasticsearch not being available to pipeline failing to install. --- filebeat/tests/system/test_modules.py | 48 ++++++++++++++++++--------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/filebeat/tests/system/test_modules.py b/filebeat/tests/system/test_modules.py index d449258c40f..fa3caa93475 100644 --- a/filebeat/tests/system/test_modules.py +++ b/filebeat/tests/system/test_modules.py @@ -131,22 +131,29 @@ def run_on_file(self, module, fileset, test_file, cfgfile): cmd.append("{module}.{fileset}.var.format=json".format(module=module, fileset=fileset)) output_path = os.path.join(self.working_dir) - output = open(os.path.join(output_path, "output.log"), "ab") - output.write(bytes(" ".join(cmd) + "\n", "utf-8")) - - # Use a fixed timezone so results don't vary depending on the environment - # Don't use UTC to avoid hiding that non-UTC timezones are not being converted as needed, - # this can happen because UTC uses to be the default timezone in date parsers when no other - # timezone is specified. - local_env = os.environ.copy() - local_env["TZ"] = 'Etc/GMT+2' - - subprocess.Popen(cmd, - env=local_env, - stdin=None, - stdout=output, - stderr=subprocess.STDOUT, - bufsize=0).wait() + # Runs inside a with block to ensure file is closed afterwards + with open(os.path.join(output_path, "output.log"), "ab") as output: + output.write(bytes(" ".join(cmd) + "\n", "utf-8")) + + # Use a fixed timezone so results don't vary depending on the environment + # Don't use UTC to avoid hiding that non-UTC timezones are not being converted as needed, + # this can happen because UTC uses to be the default timezone in date parsers when no other + # timezone is specified. + local_env = os.environ.copy() + local_env["TZ"] = 'Etc/GMT+2' + + subprocess.Popen(cmd, + env=local_env, + stdin=None, + stdout=output, + stderr=subprocess.STDOUT, + bufsize=0).wait() + + # List of errors to check in filebeat output logs + errors = ["Error loading pipeline for fileset"] + # Checks if the output of filebeat includes errors + contains_error, error_line = file_contains(os.path.join(output_path, "output.log"), errors) + assert contains_error is False, "Error found in log:{}".format(error_line) # Make sure index exists self.wait_until(lambda: self.es.indices.exists(self.index_name)) @@ -305,5 +312,14 @@ def delete_key(obj, key): del obj[key] +def file_contains(filepath, strings): + with open(filepath, 'r') as file: + for line in file: + for string in strings: + if string in line: + return True, line + return False, None + + def pretty_json(obj): return json.dumps(obj, indent=2, separators=(',', ': ')) From 804db767212ff046c93ca77d48eca1716f08b7f2 Mon Sep 17 00:00:00 2001 From: Marius Iversen Date: Tue, 6 Oct 2020 11:52:06 +0200 Subject: [PATCH 30/93] [Filebeat][New Module] Add support for Microsoft MTP / 365 Defender (#21446) * Initial commit for mtp mvp * first finished MVP version of MTP module * updating m365_defender with new fields and new name * reverting some files that shouldnt be added * removing dhcp generated logs from PR * converting two fields to strings and updating some default template configurations * adding changelog entry * Initial commit for mtp mvp * first finished MVP version of MTP module * updating m365_defender with new fields and new name * reverting some files that shouldnt be added * removing dhcp generated logs from PR * converting two fields to strings and updating some default template configurations * adding changelog entry * updating typo Co-authored-by: Marc Guasch --- CHANGELOG.next.asciidoc | 1 + filebeat/docs/fields.asciidoc | 427 ++++++++++++ filebeat/docs/modules/microsoft.asciidoc | 83 ++- x-pack/filebeat/filebeat.reference.yml | 15 +- .../module/microsoft/_meta/config.yml | 15 +- .../module/microsoft/_meta/docs.asciidoc | 83 ++- x-pack/filebeat/module/microsoft/fields.go | 2 +- .../microsoft/m365_defender/_meta/fields.yml | 176 +++++ .../m365_defender/config/defender.yml | 43 ++ .../m365_defender/ingest/pipeline.yml | 301 +++++++++ .../microsoft/m365_defender/manifest.yml | 17 + .../test/m365_defender-test.ndjson.log | 9 + ...365_defender-test.ndjson.log-expected.json | 611 ++++++++++++++++++ .../filebeat/modules.d/microsoft.yml.disabled | 15 +- 14 files changed, 1790 insertions(+), 8 deletions(-) create mode 100644 x-pack/filebeat/module/microsoft/m365_defender/_meta/fields.yml create mode 100644 x-pack/filebeat/module/microsoft/m365_defender/config/defender.yml create mode 100644 x-pack/filebeat/module/microsoft/m365_defender/ingest/pipeline.yml create mode 100644 x-pack/filebeat/module/microsoft/m365_defender/manifest.yml create mode 100644 x-pack/filebeat/module/microsoft/m365_defender/test/m365_defender-test.ndjson.log create mode 100644 x-pack/filebeat/module/microsoft/m365_defender/test/m365_defender-test.ndjson.log-expected.json diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 07e7bc4be0d..c86fc85e347 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -608,6 +608,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Convert aws s3 to v2 input {pull}20005[20005] - New Cisco Umbrella dataset {pull}21504[21504] - New juniper.srx dataset for Juniper SRX logs. {pull}20017[20017] +- Adding support for Microsoft 365 Defender (Microsoft Threat Protection) {pull}21446[21446] *Heartbeat* diff --git a/filebeat/docs/fields.asciidoc b/filebeat/docs/fields.asciidoc index a2f19000095..e7c2d35ff37 100644 --- a/filebeat/docs/fields.asciidoc +++ b/filebeat/docs/fields.asciidoc @@ -95523,6 +95523,433 @@ type: keyword -- +[float] +=== microsoft.m365_defender + +Module for ingesting Microsoft Defender ATP. + + + +*`microsoft.m365_defender.incidentId`*:: ++ +-- +Unique identifier to represent the incident. + + +type: keyword + +-- + +*`microsoft.m365_defender.redirectIncidentId`*:: ++ +-- +Only populated in case an incident is being grouped together with another incident, as part of the incident processing logic. + + +type: keyword + +-- + +*`microsoft.m365_defender.incidentName`*:: ++ +-- +Name of the Incident. + + +type: keyword + +-- + +*`microsoft.m365_defender.determination`*:: ++ +-- +Specifies the determination of the incident. The property values are: NotAvailable, Apt, Malware, SecurityPersonnel, SecurityTesting, UnwantedSoftware, Other. + + +type: keyword + +-- + +*`microsoft.m365_defender.investigationState`*:: ++ +-- +The current state of the Investigation. + + +type: keyword + +-- + +*`microsoft.m365_defender.assignedTo`*:: ++ +-- +Owner of the alert. + + +type: keyword + +-- + +*`microsoft.m365_defender.tags`*:: ++ +-- +Array of custom tags associated with an incident, for example to flag a group of incidents with a common characteristic. + + +type: keyword + +-- + +*`microsoft.m365_defender.status`*:: ++ +-- +Specifies the current status of the alert. Possible values are: 'Unknown', 'New', 'InProgress' and 'Resolved'. + + +type: keyword + +-- + +*`microsoft.m365_defender.classification`*:: ++ +-- +Specification of the alert. Possible values are: 'Unknown', 'FalsePositive', 'TruePositive'. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.incidentId`*:: ++ +-- +Unique identifier to represent the incident this alert is associated with. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.resolvedTime`*:: ++ +-- +Time when alert was resolved. + + +type: date + +-- + +*`microsoft.m365_defender.alerts.status`*:: ++ +-- +Categorize alerts (as New, Active, or Resolved). + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.severity`*:: ++ +-- +The severity of the related alert. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.creationTime`*:: ++ +-- +Time when alert was first created. + + +type: date + +-- + +*`microsoft.m365_defender.alerts.lastUpdatedTime`*:: ++ +-- +Time when alert was last updated. + + +type: date + +-- + +*`microsoft.m365_defender.alerts.investigationId`*:: ++ +-- +The automated investigation id triggered by this alert. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.userSid`*:: ++ +-- +The SID of the related user + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.detectionSource`*:: ++ +-- +The service that initially detected the threat. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.classification`*:: ++ +-- +The specification for the incident. The property values are: Unknown, FalsePositive, TruePositive or null. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.investigationState`*:: ++ +-- +Information on the investigation's current status. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.determination`*:: ++ +-- +Specifies the determination of the incident. The property values are: NotAvailable, Apt, Malware, SecurityPersonnel, SecurityTesting, UnwantedSoftware, Other or null + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.assignedTo`*:: ++ +-- +Owner of the incident, or null if no owner is assigned. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.actorName`*:: ++ +-- +The activity group, if any, the associated with this alert. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.threatFamilyName`*:: ++ +-- +Threat family associated with this alert. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.mitreTechniques`*:: ++ +-- +The attack techniques, as aligned with the MITRE ATT&CK™ framework. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.entities.entityType`*:: ++ +-- +Entities that have been identified to be part of, or related to, a given alert. The properties values are: User, Ip, Url, File, Process, MailBox, MailMessage, MailCluster, Registry. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.entities.accountName`*:: ++ +-- +Account name of the related user. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.entities.mailboxDisplayName`*:: ++ +-- +The display name of the related mailbox. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.entities.mailboxAddress`*:: ++ +-- +The mail address of the related mailbox. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.entities.clusterBy`*:: ++ +-- +A list of metadata if the entityType is MailCluster. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.entities.sender`*:: ++ +-- +The sender for the related email message. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.entities.recipient`*:: ++ +-- +The recipient for the related email message. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.entities.subject`*:: ++ +-- +The subject for the related email message. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.entities.deliveryAction`*:: ++ +-- +The delivery status for the related email message. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.entities.securityGroupId`*:: ++ +-- +The Security Group ID for the user related to the email message. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.entities.securityGroupName`*:: ++ +-- +The Security Group Name for the user related to the email message. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.entities.registryHive`*:: ++ +-- +Reference to which Hive in registry the event is related to, if eventType is registry. Example: HKEY_LOCAL_MACHINE. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.entities.registryKey`*:: ++ +-- +Reference to the related registry key to the event. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.entities.registryValueType`*:: ++ +-- +Value type of the registry key/value pair related to the event. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.entities.deviceId`*:: ++ +-- +The unique ID of the device related to the event. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.entities.ipAddress`*:: ++ +-- +The related IP address to the event. + + +type: keyword + +-- + +*`microsoft.m365_defender.alerts.devices`*:: ++ +-- +The devices related to the investigation. + + +type: flattened + +-- + [[exported-fields-misp]] == MISP fields diff --git a/filebeat/docs/modules/microsoft.asciidoc b/filebeat/docs/modules/microsoft.asciidoc index 513ca155be6..5edbbf027d0 100644 --- a/filebeat/docs/modules/microsoft.asciidoc +++ b/filebeat/docs/modules/microsoft.asciidoc @@ -12,7 +12,8 @@ This file is generated! See scripts/docs_collector.py This is a module for ingesting data from the different Microsoft Products. Currently supports these filesets: -- `defender_atp` fileset: Supports Microsoft Defender ATP +- `defender_atp` fileset: Supports Microsoft Defender for Endpoint (Microsoft Defender ATP) +- `m365_defender` fileset: Supports Microsoft 365 Defender (Microsoft Threat Protection) - `dhcp` fileset: Supports Microsoft DHCP logs include::../include/what-happens.asciidoc[] @@ -25,6 +26,84 @@ include::../include/configuring-intro.asciidoc[] include::../include/config-option-intro.asciidoc[] +[float] +==== `m365_defender` fileset settings + +beta[] + +To configure access for Filebeat to Microsoft 365 Defender you will have to create a new Azure Application registration, this will again return Oauth tokens with access to the Microsoft 365 Defender API + +The procedure to create an application is found on the below link: + +https://docs.microsoft.com/en-us/microsoft-365/security/mtp/api-create-app-web?view=o365-worldwide#create-an-app[Create a new Azure Application] + +When giving the application the API permissions described in the documentation (Incident.Read.All) it will only grant access to read Incidents from 365 Defender and nothing else in the Azure Domain. + +After the application has been created, it should contain 3 values that you need to apply to the module configuration. + +These values are: + +- Client ID +- Client Secret +- Tenant ID + +Example config: + +[source,yaml] +---- +- module: microsoft + m365_defender: + enabled: true + var.oauth2.client.id: "123abc-879546asd-349587-ad64508" + var.oauth2.client.secret: "980453~-Sg99gedf" + var.oauth2.token_url: "https://login.microsoftonline.com/INSERT-TENANT-ID/oauth2/token" +---- + +*`var.oauth2.client.id`*:: + +This is the client ID related to creating a new application on Azure. + +*`var.oauth2.client.secret`*:: + +The secret related to the client ID. + +*`var.oauth2.token_url`*:: + +A predefined URL towards the Oauth2 service for Microsoft. The URL should always be the same with the exception of the Tenant ID that needs to be added to the full URL. + +[float] +==== 365 Defender ECS fields + +This is a list of 365 Defender fields that are mapped to ECS. + +[options="header"] +|====================================================================== +| 365 Defender Fields | ECS Fields | +| lastUpdateTime | @timestamp | +| severity | event.severity | +| createdTime | event.created | +| alerts.category | threat.technique.name | +| alerts.description | rule.description | +| alerts.serviceSource | event.provider | +| alerts.alertId | event.id | +| alerts.firstActivity | event.start | +| alerts.lastActivity | event.end | +| alerts.title | message | +| entities.processId | process.pid | +| entities.processCommandLine | process.command_line | +| entities.processCreationTime | process.start | +| entities.parentProcessId | process.parent.pid | +| entities.parentProcessCreationTime | process.parent.start | +| entities.sha1 | file.hash.sha1 | +| entities.sha256 | file.hash.sha256 | +| entities.url | url.full | +| entities.filePath | file.path | +| entities.fileName | file.name | +| entities.userPrincipalName | host.user.name | +| entities.domainName | host.user.domain | +| entities.aadUserId | host.user.id | +|====================================================================== + [float] ==== `defender_atp` fileset settings @@ -114,7 +193,7 @@ This module comes with a sample dashboard for Defender ATP. [role="screenshot"] image::./images/filebeat-defender-atp-overview.png[] -The best way to view Defender ATP events and alert data is in the SIEM. +The best way to view Defender ATP events and alert data is in the SIEM. [role="screenshot"] image::./images/siem-alerts-cs.jpg[] diff --git a/x-pack/filebeat/filebeat.reference.yml b/x-pack/filebeat/filebeat.reference.yml index cc994b45cac..2ffff82135e 100644 --- a/x-pack/filebeat/filebeat.reference.yml +++ b/x-pack/filebeat/filebeat.reference.yml @@ -1118,7 +1118,20 @@ filebeat.modules: # Oauth Client Secret #var.oauth2.client.secret: "" - + + # Oauth Token URL, should include the tenant ID + #var.oauth2.token_url: "https://login.microsoftonline.com/TENANT-ID/oauth2/token" + m365_defender: + enabled: true + # How often the API should be polled + #var.interval: 5m + + # Oauth Client ID + #var.oauth2.client.id: "" + + # Oauth Client Secret + #var.oauth2.client.secret: "" + # Oauth Token URL, should include the tenant ID #var.oauth2.token_url: "https://login.microsoftonline.com/TENANT-ID/oauth2/token" dhcp: diff --git a/x-pack/filebeat/module/microsoft/_meta/config.yml b/x-pack/filebeat/module/microsoft/_meta/config.yml index 8e793bd2f9c..ee06eea9228 100644 --- a/x-pack/filebeat/module/microsoft/_meta/config.yml +++ b/x-pack/filebeat/module/microsoft/_meta/config.yml @@ -10,7 +10,20 @@ # Oauth Client Secret #var.oauth2.client.secret: "" - + + # Oauth Token URL, should include the tenant ID + #var.oauth2.token_url: "https://login.microsoftonline.com/TENANT-ID/oauth2/token" + m365_defender: + enabled: true + # How often the API should be polled + #var.interval: 5m + + # Oauth Client ID + #var.oauth2.client.id: "" + + # Oauth Client Secret + #var.oauth2.client.secret: "" + # Oauth Token URL, should include the tenant ID #var.oauth2.token_url: "https://login.microsoftonline.com/TENANT-ID/oauth2/token" dhcp: diff --git a/x-pack/filebeat/module/microsoft/_meta/docs.asciidoc b/x-pack/filebeat/module/microsoft/_meta/docs.asciidoc index 8a3facdc259..7e646e1b4fe 100644 --- a/x-pack/filebeat/module/microsoft/_meta/docs.asciidoc +++ b/x-pack/filebeat/module/microsoft/_meta/docs.asciidoc @@ -7,7 +7,8 @@ This is a module for ingesting data from the different Microsoft Products. Currently supports these filesets: -- `defender_atp` fileset: Supports Microsoft Defender ATP +- `defender_atp` fileset: Supports Microsoft Defender for Endpoint (Microsoft Defender ATP) +- `m365_defender` fileset: Supports Microsoft 365 Defender (Microsoft Threat Protection) - `dhcp` fileset: Supports Microsoft DHCP logs include::../include/what-happens.asciidoc[] @@ -20,6 +21,84 @@ include::../include/configuring-intro.asciidoc[] include::../include/config-option-intro.asciidoc[] +[float] +==== `m365_defender` fileset settings + +beta[] + +To configure access for Filebeat to Microsoft 365 Defender you will have to create a new Azure Application registration, this will again return Oauth tokens with access to the Microsoft 365 Defender API + +The procedure to create an application is found on the below link: + +https://docs.microsoft.com/en-us/microsoft-365/security/mtp/api-create-app-web?view=o365-worldwide#create-an-app[Create a new Azure Application] + +When giving the application the API permissions described in the documentation (Incident.Read.All) it will only grant access to read Incidents from 365 Defender and nothing else in the Azure Domain. + +After the application has been created, it should contain 3 values that you need to apply to the module configuration. + +These values are: + +- Client ID +- Client Secret +- Tenant ID + +Example config: + +[source,yaml] +---- +- module: microsoft + m365_defender: + enabled: true + var.oauth2.client.id: "123abc-879546asd-349587-ad64508" + var.oauth2.client.secret: "980453~-Sg99gedf" + var.oauth2.token_url: "https://login.microsoftonline.com/INSERT-TENANT-ID/oauth2/token" +---- + +*`var.oauth2.client.id`*:: + +This is the client ID related to creating a new application on Azure. + +*`var.oauth2.client.secret`*:: + +The secret related to the client ID. + +*`var.oauth2.token_url`*:: + +A predefined URL towards the Oauth2 service for Microsoft. The URL should always be the same with the exception of the Tenant ID that needs to be added to the full URL. + +[float] +==== 365 Defender ECS fields + +This is a list of 365 Defender fields that are mapped to ECS. + +[options="header"] +|====================================================================== +| 365 Defender Fields | ECS Fields | +| lastUpdateTime | @timestamp | +| severity | event.severity | +| createdTime | event.created | +| alerts.category | threat.technique.name | +| alerts.description | rule.description | +| alerts.serviceSource | event.provider | +| alerts.alertId | event.id | +| alerts.firstActivity | event.start | +| alerts.lastActivity | event.end | +| alerts.title | message | +| entities.processId | process.pid | +| entities.processCommandLine | process.command_line | +| entities.processCreationTime | process.start | +| entities.parentProcessId | process.parent.pid | +| entities.parentProcessCreationTime | process.parent.start | +| entities.sha1 | file.hash.sha1 | +| entities.sha256 | file.hash.sha256 | +| entities.url | url.full | +| entities.filePath | file.path | +| entities.fileName | file.name | +| entities.userPrincipalName | host.user.name | +| entities.domainName | host.user.domain | +| entities.aadUserId | host.user.id | +|====================================================================== + [float] ==== `defender_atp` fileset settings @@ -109,7 +188,7 @@ This module comes with a sample dashboard for Defender ATP. [role="screenshot"] image::./images/filebeat-defender-atp-overview.png[] -The best way to view Defender ATP events and alert data is in the SIEM. +The best way to view Defender ATP events and alert data is in the SIEM. [role="screenshot"] image::./images/siem-alerts-cs.jpg[] diff --git a/x-pack/filebeat/module/microsoft/fields.go b/x-pack/filebeat/module/microsoft/fields.go index 2576fcb8ac7..d76c98c273d 100644 --- a/x-pack/filebeat/module/microsoft/fields.go +++ b/x-pack/filebeat/module/microsoft/fields.go @@ -19,5 +19,5 @@ func init() { // AssetMicrosoft returns asset data. // This is the base64 encoded gzipped contents of module/microsoft. func AssetMicrosoft() string { - return "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" + return "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" } diff --git a/x-pack/filebeat/module/microsoft/m365_defender/_meta/fields.yml b/x-pack/filebeat/module/microsoft/m365_defender/_meta/fields.yml new file mode 100644 index 00000000000..3656cd7c1de --- /dev/null +++ b/x-pack/filebeat/module/microsoft/m365_defender/_meta/fields.yml @@ -0,0 +1,176 @@ +- name: microsoft.m365_defender + type: group + release: beta + default_field: false + description: > + Module for ingesting Microsoft Defender ATP. + fields: + - name: incidentId + type: keyword + description: > + Unique identifier to represent the incident. + - name: redirectIncidentId + type: keyword + description: > + Only populated in case an incident is being grouped together with another incident, as part of the incident processing logic. + - name: incidentName + type: keyword + description: > + Name of the Incident. + - name: determination + type: keyword + description: > + Specifies the determination of the incident. The property values are: NotAvailable, Apt, Malware, SecurityPersonnel, SecurityTesting, UnwantedSoftware, Other. + - name: investigationState + type: keyword + description: > + The current state of the Investigation. + - name: assignedTo + type: keyword + description: > + Owner of the alert. + - name: tags + type: keyword + description: > + Array of custom tags associated with an incident, for example to flag a group of incidents with a common characteristic. + - name: status + type: keyword + description: > + Specifies the current status of the alert. Possible values are: 'Unknown', 'New', 'InProgress' and 'Resolved'. + - name: classification + type: keyword + description: > + Specification of the alert. Possible values are: 'Unknown', 'FalsePositive', 'TruePositive'. + - name: alerts.incidentId + type: keyword + description: > + Unique identifier to represent the incident this alert is associated with. + - name: alerts.resolvedTime + type: date + description: > + Time when alert was resolved. + - name: alerts.status + type: keyword + description: > + Categorize alerts (as New, Active, or Resolved). + - name: alerts.severity + type: keyword + description: > + The severity of the related alert. + - name: alerts.creationTime + type: date + description: > + Time when alert was first created. + - name: alerts.lastUpdatedTime + type: date + description: > + Time when alert was last updated. + - name: alerts.investigationId + type: keyword + description: > + The automated investigation id triggered by this alert. + - name: alerts.userSid + type: keyword + description: > + The SID of the related user + - name: alerts.detectionSource + type: keyword + description: > + The service that initially detected the threat. + - name: alerts.classification + type: keyword + description: > + The specification for the incident. The property values are: Unknown, FalsePositive, TruePositive or null. + - name: alerts.investigationState + type: keyword + description: > + Information on the investigation's current status. + - name: alerts.determination + type: keyword + description: > + Specifies the determination of the incident. The property values are: NotAvailable, Apt, Malware, SecurityPersonnel, SecurityTesting, UnwantedSoftware, Other or null + - name: alerts.assignedTo + type: keyword + description: > + Owner of the incident, or null if no owner is assigned. + - name: alerts.actorName + type: keyword + description: > + The activity group, if any, the associated with this alert. + - name: alerts.threatFamilyName + type: keyword + description: > + Threat family associated with this alert. + - name: alerts.mitreTechniques + type: keyword + description: > + The attack techniques, as aligned with the MITRE ATT&CK™ framework. + - name: alerts.entities.entityType + type: keyword + description: > + Entities that have been identified to be part of, or related to, a given alert. The properties values are: User, Ip, Url, File, Process, MailBox, MailMessage, MailCluster, Registry. + - name: alerts.entities.accountName + type: keyword + description: > + Account name of the related user. + - name: alerts.entities.mailboxDisplayName + type: keyword + description: > + The display name of the related mailbox. + - name: alerts.entities.mailboxAddress + type: keyword + description: > + The mail address of the related mailbox. + - name: alerts.entities.clusterBy + type: keyword + description: > + A list of metadata if the entityType is MailCluster. + - name: alerts.entities.sender + type: keyword + description: > + The sender for the related email message. + - name: alerts.entities.recipient + type: keyword + description: > + The recipient for the related email message. + - name: alerts.entities.subject + type: keyword + description: > + The subject for the related email message. + - name: alerts.entities.deliveryAction + type: keyword + description: > + The delivery status for the related email message. + - name: alerts.entities.securityGroupId + type: keyword + description: > + The Security Group ID for the user related to the email message. + - name: alerts.entities.securityGroupName + type: keyword + description: > + The Security Group Name for the user related to the email message. + - name: alerts.entities.registryHive + type: keyword + description: > + Reference to which Hive in registry the event is related to, if eventType is registry. Example: HKEY_LOCAL_MACHINE. + - name: alerts.entities.registryKey + type: keyword + description: > + Reference to the related registry key to the event. + - name: alerts.entities.registryValueType + type: keyword + description: > + Value type of the registry key/value pair related to the event. + - name: alerts.entities.deviceId + type: keyword + description: > + The unique ID of the device related to the event. + - name: alerts.entities.ipAddress + type: keyword + description: > + The related IP address to the event. + - name: alerts.devices + type: flattened + description: > + The devices related to the investigation. + diff --git a/x-pack/filebeat/module/microsoft/m365_defender/config/defender.yml b/x-pack/filebeat/module/microsoft/m365_defender/config/defender.yml new file mode 100644 index 00000000000..2b2a6f936f7 --- /dev/null +++ b/x-pack/filebeat/module/microsoft/m365_defender/config/defender.yml @@ -0,0 +1,43 @@ +{{ if eq .input "httpjson" }} + +type: httpjson +http_method: GET +interval: {{ .interval }} +json_objects_array: value +split_events_by: alerts..entities +url: {{ .url }} + +oauth2: {{ .oauth2 | tojson }} +oauth2.provider: azure +oauth2.azure.resource: https://api.security.microsoft.com +http_headers: + User-Agent: MdatpPartner-Elastic-Filebeat/1.0.0 +date_cursor.field: lastUpdateTime +date_cursor.url_field: '$filter' +date_cursor.value_template: 'lastUpdateTime gt {{.}}' +date_cursor.initial_interval: 55m +date_cursor.date_format: '2006-01-02T15:04:05.9999999Z' + + +{{ else if eq .input "file" }} + +type: log +paths: +{{ range $i, $path := .paths }} + - {{$path}} +{{ end }} +exclude_files: [".gz$"] + +{{ end }} + +tags: {{ .tags | tojson }} +publisher_pipeline.disable_host: {{ inList .tags "forwarded" }} + +processors: + - decode_json_fields: + fields: [message] + target: json + - add_fields: + target: '' + fields: + ecs.version: 1.6.0 diff --git a/x-pack/filebeat/module/microsoft/m365_defender/ingest/pipeline.yml b/x-pack/filebeat/module/microsoft/m365_defender/ingest/pipeline.yml new file mode 100644 index 00000000000..f1ea7c03abd --- /dev/null +++ b/x-pack/filebeat/module/microsoft/m365_defender/ingest/pipeline.yml @@ -0,0 +1,301 @@ +--- +description: Pipeline for parsing microsoft atp logs +processors: +- set: + field: event.ingested + value: '{{_ingest.timestamp}}' +- remove: + field: + - message + - json.comments + - host + ignore_missing: true + +######################### +## ECS General Mapping ## +######################### +- script: + lang: painless + if: ctx?.json != null + source: | + void handleMap(Map map) { + for (def x : map.values()) { + if (x instanceof Map) { + handleMap(x); + } else if (x instanceof List) { + handleList(x); + } + } + map.values().removeIf(v -> v == null); + } + void handleList(List list) { + for (def x : list) { + if (x instanceof Map) { + handleMap(x); + } else if (x instanceof List) { + handleList(x); + } + } + } + handleMap(ctx); + +- set: + field: cloud.provider + value: azure +- set: + field: '@timestamp' + value: '{{json.lastUpdateTime}}' + if: ctx.json?.lastUpdateTime != null +- rename: + field: json.alerts.title + target_field: message + ignore_missing: true + +####################### +## ECS Event Mapping ## +####################### +- set: + field: event.kind + value: alert +# Events returned from the API is always in UTC, so should never use anything else +- set: + field: event.timezone + value: UTC +- set: + field: event.action + value: '{{json.alerts.category}}' + if: ctx.json?.alerts?.category != null +- set: + field: event.provider + value: '{{json.alerts.serviceSource}}' + if: ctx.json?.alerts?.serviceSource != null +- set: + field: event.created + value: '{{json.createdTime}}' + if: ctx.json?.createdTime != null +- append: + field: event.category + value: host +- append: + field: event.category + value: malware + if: ctx.json?.determination == 'Malware' +- append: + field: event.category + value: process + if: ctx.json?.entities?.entityType == 'Process' +- append: + field: event.type + value: user + if: ctx.json?.entities?.entityType == 'User' +- append: + field: event.type + value: + - creation + - start + if: ctx.json?.status == 'New' +- append: + field: event.type + value: end + if: ctx.json?.status == 'Resolved' +- rename: + field: json.alerts.alertId + target_field: event.id + ignore_missing: true +- rename: + field: json.alerts.firstActivity + target_field: event.start + ignore_missing: true +- rename: + field: json.alerts.lastActivity + target_field: event.end + ignore_missing: true +- set: + field: event.severity + value: 0 + if: ctx.json?.severity == 'Unspecified' +- set: + field: event.severity + value: 1 + if: ctx.json?.severity == 'Informational' +- set: + field: event.severity + value: 2 + if: ctx.json?.severity == 'Low' +- set: + field: event.severity + value: 3 + if: ctx.json?.severity == 'Medium' +- set: + field: event.severity + value: 4 + if: ctx.json?.severity == 'High' +- script: + lang: painless + if: "ctx?.event?.start != null && ctx?.event?.end != null" + source: > + Instant eventstart = ZonedDateTime.parse(ctx?.event?.start).toInstant(); + Instant eventend = ZonedDateTime.parse(ctx?.event?.end).toInstant(); + ctx.event['duration'] = ChronoUnit.NANOS.between(eventstart, eventend); + +######################## +## ECS Threat Mapping ## +######################## +- set: + field: threat.framework + value: MITRE ATT&CK + if: ctx.json?.alerts?.category != null +- rename: + field: json.alerts.category + target_field: threat.technique.name + ignore_missing: true +- rename: + field: json.alerts.description + target_field: rule.description + ignore_missing: true + if: ctx.json?.alerts?.description.length() < 1020 + +###################### +## ECS File Mapping ## +###################### +- rename: + field: json.alerts.entities.fileName + target_field: file.name + ignore_missing: true +- rename: + field: json.alerts.entities.sha256 + target_field: file.hash.sha256 + ignore_missing: true +- rename: + field: json.alerts.entities.sha1 + target_field: file.hash.sha1 + ignore_missing: true +- rename: + field: json.alerts.entities.filePath + target_field: file.path + ignore_missing: true + +######################### +## ECS Process Mapping ## +######################### +- rename: + field: json.alerts.entities.processId + target_field: process.pid + ignore_missing: true +- rename: + field: json.alerts.entities.processCommandLine + target_field: process.command_line + ignore_missing: true +- rename: + field: json.alerts.entities.processCreationTime + target_field: process.start + ignore_missing: true +- rename: + field: json.alerts.entities.parentProcessId + target_field: process.parent.pid + ignore_missing: true +- rename: + field: json.alerts.entities.parentProcessCreationTime + target_field: process.parent.start + ignore_missing: true + +########################## +## ECS Observer Mapping ## +########################## +- set: + field: observer.product + value: 365 Defender +- set: + field: observer.vendor + value: Microsoft +- rename: + field: json.alerts.serviceSource + target_field: observer.name + ignore_missing: true + +##################### +## ECS URL Mapping ## +##################### +- rename: + field: json.alerts.entities.url + target_field: url.full + ignore_missing: true + if: ctx?.json?.entities?.url != null + +###################### +## ECS User Mapping ## +###################### +- rename: + field: json.alerts.entities.userPrincipalName + target_field: host.user.name + ignore_missing: true +- rename: + field: json.alerts.entities.domainName + target_field: host.user.domain + ignore_missing: true +- rename: + field: json.alerts.entities.aadUserId + target_field: host.user.id + ignore_missing: true + +######################### +## ECS Related Mapping ## +######################### +- append: + field: related.ip + value: '{{json.alerts.entities.ipAddress}}' + if: ctx.json?.entities?.ipAddress != null +- append: + field: related.user + value: '{{host.user.name}}' + if: ctx.host?.user?.name != null +- append: + field: related.hash + value: '{{file.hash.sha1}}' + if: ctx.file?.hash?.sha1 != null +- append: + field: related.hash + value: '{{file.hash.sha256}}' + if: ctx.file?.hash?.sha256 != null +- append: + field: related.hosts + value: '{{host.hostname}}' + if: ctx.host?.hostname != null + +############# +## Cleanup ## +############# +- convert: + field: json.alerts.incidentId + type: string + ignore_missing: true +- convert: + field: json.incidentId + type: string + ignore_missing: true +- remove: + field: json.alerts.mitreTechniques + ignore_missing: true + if: ctx?.json?.alerts?.mitreTechniques.isEmpty() +- remove: + field: json.alerts.devices + ignore_missing: true + if: ctx?.json?.alerts?.devices.isEmpty() +- remove: + field: json.tags + ignore_missing: true + if: ctx?.json?.tags.isEmpty() +- remove: + ignore_missing: true + field: + - json.createdTime + - json.severity + - json.lastUpdateTime +- rename: + field: json + target_field: microsoft.m365_defender + ignore_missing: true +on_failure: +- set: + field: error.message + value: '{{_ingest.on_failure_message}}' diff --git a/x-pack/filebeat/module/microsoft/m365_defender/manifest.yml b/x-pack/filebeat/module/microsoft/m365_defender/manifest.yml new file mode 100644 index 00000000000..d7b73352f79 --- /dev/null +++ b/x-pack/filebeat/module/microsoft/m365_defender/manifest.yml @@ -0,0 +1,17 @@ +module_version: 1.0 + +var: + - name: input + default: httpjson + - name: interval + default: 5m + - name: tags + default: [m365-defender, forwarded] + - name: url + default: "https://api.security.microsoft.com/api/incidents" + - name: oauth2 + +ingest_pipeline: ingest/pipeline.yml +input: config/defender.yml + + diff --git a/x-pack/filebeat/module/microsoft/m365_defender/test/m365_defender-test.ndjson.log b/x-pack/filebeat/module/microsoft/m365_defender/test/m365_defender-test.ndjson.log new file mode 100644 index 00000000000..4fc4cf141b6 --- /dev/null +++ b/x-pack/filebeat/module/microsoft/m365_defender/test/m365_defender-test.ndjson.log @@ -0,0 +1,9 @@ +{"status":"Resolved","classification":"Unknown","createdTime":"2020-06-30T09:32:31.85Z","determination":"NotAvailable","incidentId":12,"incidentName":"12","redirectIncidentId":null,"severity":"Low","alerts":{"creationTime":"2020-06-30T09:32:31.4579225Z","detectionSource":"WindowsDefenderAv","firstActivity":"2020-06-30T09:31:22.5729558Z","incidentId":12,"serviceSource":"MicrosoftDefenderATP","actorName":null,"alertId":"da637291063515066999_-2102938302","determination":null,"lastActivity":"2020-06-30T09:46:15.0876676Z","assignedTo":"Automation","devices":[{"osBuild":17763,"osProcessor":"x64","rbacGroupId":0,"aadDeviceId":null,"firstSeen":"2020-06-30T08:55:08.8320449Z","mdatpDeviceId":"75a63a39f9bc5a964f417c11f6277d5bf9489f0d","rbacGroupName":null,"riskScore":"High","version":"Other","deviceDnsName":"TestServer4","healthStatus":"Inactive","osPlatform":"Other"}],"investigationId":9,"threatFamilyName":null,"title":"'Mountsi' malware was detected","category":"Malware","classification":null,"description":"Malware and unwanted software are undesirable applications that perform annoying, disruptive, or harmful actions on affected machines. Some of these undesirable applications can replicate and spread from one machine to another. Others are able to receive commands from remote attackers and perform activities associated with cyber attacks.\n\nThis detection might indicate that the malware was stopped from delivering its payload. However, it is prudent to check the machine for signs of infection.","entities":{"deviceId":"75a63a39f9bc5a964f417c11f6277d5bf9489f0d","entityType":"File","fileName":"amsistream-1D89ECED25A52AB98B76FF619B7BA07A","sha1":"ffb1670c6c6a9c5b4c5cea8b6b8e68d62e7ff281","sha256":"fd46705c4f67a8ef16e76259ca6d6253241e51a1f8952223145f92aa1907d356"},"investigationState":"Benign","lastUpdatedTime":"2020-08-26T09:41:27.7233333Z","mitreTechniques":[],"resolvedTime":"2020-06-30T11:13:12.2680434Z","severity":"Informational","status":"Resolved"},"assignedTo":"elastic@elasticuser.com","lastUpdateTime":"2020-09-23T19:44:36.29Z","tags":[]} +{"incidentName":"12","redirectIncidentId":null,"severity":"Low","status":"Resolved","tags":[],"alerts":{"devices":[{"aadDeviceId":null,"firstSeen":"2020-06-30T08:55:08.8320449Z","healthStatus":"Inactive","osPlatform":"Other","rbacGroupId":0,"rbacGroupName":null,"deviceDnsName":"TestServer4","mdatpDeviceId":"75a63a39f9bc5a964f417c11f6277d5bf9489f0d","osBuild":17763,"osProcessor":"x64","riskScore":"High","version":"Other"}],"entities":{"deviceId":"75a63a39f9bc5a964f417c11f6277d5bf9489f0d","entityType":"File","fileName":"amsistream-B103C1A335BDB049E617D1AC4A41FCDC","sha1":"ffb1670c6c6a9c5b4c5cea8b6b8e68d62e7ff281","sha256":"fd46705c4f67a8ef16e76259ca6d6253241e51a1f8952223145f92aa1907d356"},"firstActivity":"2020-06-30T09:31:22.5729558Z","lastUpdatedTime":"2020-08-26T09:41:27.7233333Z","alertId":"da637291063515066999_-2102938302","category":"Malware","classification":null,"determination":null,"mitreTechniques":[],"serviceSource":"MicrosoftDefenderATP","status":"Resolved","assignedTo":"Automation","detectionSource":"WindowsDefenderAv","incidentId":12,"investigationId":9,"severity":"Informational","title":"'Mountsi' malware was detected","actorName":null,"description":"Malware and unwanted software are undesirable applications that perform annoying, disruptive, or harmful actions on affected machines. Some of these undesirable applications can replicate and spread from one machine to another. Others are able to receive commands from remote attackers and perform activities associated with cyber attacks.\n\nThis detection might indicate that the malware was stopped from delivering its payload. However, it is prudent to check the machine for signs of infection.","lastActivity":"2020-06-30T09:46:15.0876676Z","resolvedTime":"2020-06-30T11:13:12.2680434Z","creationTime":"2020-06-30T09:32:31.4579225Z","investigationState":"Benign","threatFamilyName":null},"assignedTo":"elastic@elasticuser.com","determination":"NotAvailable","incidentId":12,"lastUpdateTime":"2020-09-23T19:44:36.29Z","classification":"Unknown","createdTime":"2020-06-30T09:32:31.85Z"} +{"alerts":{"mitreTechniques":[],"status":"Resolved","title":"'Exeselrun' malware was detected","threatFamilyName":null,"alertId":"da637291085389812387_-1296910232","category":"Malware","detectionSource":"WindowsDefenderAv","lastUpdatedTime":"2020-08-26T09:41:27.7233333Z","serviceSource":"MicrosoftDefenderATP","severity":"Informational","resolvedTime":"2020-06-30T11:13:12.2680434Z","description":"Malware and unwanted software are undesirable applications that perform annoying, disruptive, or harmful actions on affected machines. Some of these undesirable applications can replicate and spread from one machine to another. Others are able to receive commands from remote attackers and perform activities associated with cyber attacks.\n\nThis detection might indicate that the malware was stopped from delivering its payload. However, it is prudent to check the machine for signs of infection.","devices":[{"deviceDnsName":"testserver4","healthStatus":"Inactive","osProcessor":"x64","rbacGroupId":0,"riskScore":"High","version":"Other","aadDeviceId":null,"firstSeen":"2020-06-30T08:55:08.8320449Z","mdatpDeviceId":"75a63a39f9bc5a964f417c11f6277d5bf9489f0d","osBuild":17763,"osPlatform":"Other","rbacGroupName":null}],"firstActivity":"2020-06-30T10:07:44.3144099Z","incidentId":12,"investigationId":9,"investigationState":"Benign","lastActivity":"2020-06-30T10:07:44.3144099Z","actorName":null,"assignedTo":"Automation","classification":null,"creationTime":"2020-06-30T10:08:58.9655663Z","determination":null,"entities":{"fileName":"SB.xsl","filePath":"C:\\Windows\\Temp\\sb-sim-temp-ikyxqi\\sb_10554_bs_h4qpk5","sha1":"d1bb29ce3d01d01451e3623132545d5f577a1bd6","sha256":"ce8d3a3811a3bf923902d6924532308506fe5d024435ddee0cabf90ad9b29f6a","deviceId":"75a63a39f9bc5a964f417c11f6277d5bf9489f0d","entityType":"File"}},"createdTime":"2020-06-30T09:32:31.85Z","determination":"NotAvailable","incidentId":12,"lastUpdateTime":"2020-09-23T19:44:36.29Z","redirectIncidentId":null,"assignedTo":"elastic@elasticuser.com","classification":"Unknown","incidentName":"12","severity":"Low","status":"Resolved","tags":[]} +{"classification":"Unknown","createdTime":"2020-06-30T09:32:31.85Z","determination":"NotAvailable","incidentName":"12","lastUpdateTime":"2020-09-23T19:44:36.29Z","redirectIncidentId":null,"severity":"Low","assignedTo":"elastic@elasticuser.com","status":"Resolved","incidentId":12,"tags":[],"alerts":{"assignedTo":"elastic@elasticuser.com","firstActivity":"2020-06-30T10:07:44.333733Z","investigationId":9,"mitreTechniques":[],"resolvedTime":"2020-06-30T11:13:12.2680434Z","title":"An active 'Exeselrun' malware was detected","alertId":"da637291085411733957_-1043898914","category":"Malware","classification":null,"detectionSource":"WindowsDefenderAv","determination":null,"threatFamilyName":null,"actorName":null,"serviceSource":"MicrosoftDefenderATP","status":"Resolved","creationTime":"2020-06-30T10:09:01.1569718Z","devices":[{"deviceDnsName":"TestServer4","healthStatus":"Inactive","osBuild":17763,"osProcessor":"x64","rbacGroupName":null,"riskScore":"High","version":"Other","aadDeviceId":null,"firstSeen":"2020-06-30T08:55:08.8320449Z","mdatpDeviceId":"75a63a39f9bc5a964f417c11f6277d5bf9489f0d","osPlatform":"Other","rbacGroupId":0}],"entities":{"filePath":"C:\\Windows\\Temp\\sb-sim-temp-ikyxqi\\sb_10554_bs_h4qpk5","deviceId":"75a63a39f9bc5a964f417c11f6277d5bf9489f0d","entityType":"File","fileName":"SB.xsl"},"incidentId":12,"investigationState":"Benign","lastActivity":"2020-06-30T10:07:44.333733Z","lastUpdatedTime":"2020-08-26T09:41:27.7233333Z","severity":"Low","description":"Malware and unwanted software are undesirable applications that perform annoying, disruptive, or harmful actions on affected machines. Some of these undesirable applications can replicate and spread from one machine to another. Others are able to receive commands from remote attackers and perform activities associated with cyber attacks.\n\nA malware is considered active if it is found running on the machine or it already has persistence mechanisms in place. Active malware detections are assigned higher severity ratings.\n\nBecause this malware was active, take precautionary measures and check for residual signs of infection."}} +{"assignedTo":"elastic@elasticuser.com","classification":"Unknown","createdTime":"2020-06-30T09:32:31.85Z","redirectIncidentId":null,"severity":"Low","status":"Resolved","tags":[],"alerts":{"assignedTo":"elastic@elasticuser.com","determination":null,"serviceSource":"MicrosoftDefenderATP","severity":"Low","alertId":"da637291086161511365_-2075772905","classification":"FalsePositive","creationTime":"2020-06-30T10:10:16.1355657Z","description":"Malware and unwanted software are undesirable applications that perform annoying, disruptive, or harmful actions on affected machines. Some of these undesirable applications can replicate and spread from one machine to another. Others are able to receive commands from remote attackers and perform activities associated with cyber attacks.\n\nA malware is considered active if it is found running on the machine or it already has persistence mechanisms in place. Active malware detections are assigned higher severity ratings.\n\nBecause this malware was active, take precautionary measures and check for residual signs of infection.","entities":{"deviceId":"75a63a39f9bc5a964f417c11f6277d5bf9489f0d","entityType":"Process","processCreationTime":"2020-06-30T10:31:04.1092404Z","processId":6720},"mitreTechniques":[],"title":"Suspicious 'AccessibilityEscalation' behavior was detected","category":"SuspiciousActivity","devices":[{"aadDeviceId":null,"mdatpDeviceId":"75a63a39f9bc5a964f417c11f6277d5bf9489f0d","osProcessor":"x64","riskScore":"High","osPlatform":"Other","rbacGroupId":0,"rbacGroupName":null,"version":"Other","deviceDnsName":"testserver4","firstSeen":"2020-06-30T08:55:08.8320449Z","healthStatus":"Inactive","osBuild":17763}],"firstActivity":"2020-06-30T10:09:10.8889583Z","investigationState":"UnsupportedAlertType","status":"Resolved","detectionSource":"WindowsDefenderAv","incidentId":12,"investigationId":null,"lastActivity":"2020-06-30T10:31:09.4165785Z","lastUpdatedTime":"2020-09-23T19:44:37.9666667Z","resolvedTime":"2020-09-23T19:44:36.1092821Z","threatFamilyName":null,"actorName":null},"determination":"NotAvailable","incidentId":12,"incidentName":"12","lastUpdateTime":"2020-09-23T19:44:36.29Z"} +{"determination":"NotAvailable","severity":"Low","classification":"Unknown","createdTime":"2020-06-30T09:32:31.85Z","incidentId":12,"incidentName":"12","lastUpdateTime":"2020-09-23T19:44:36.29Z","redirectIncidentId":null,"alerts":{"lastActivity":"2020-06-30T10:31:09.4165785Z","lastUpdatedTime":"2020-09-23T19:44:37.9666667Z","actorName":null,"description":"Malware and unwanted software are undesirable applications that perform annoying, disruptive, or harmful actions on affected machines. Some of these undesirable applications can replicate and spread from one machine to another. Others are able to receive commands from remote attackers and perform activities associated with cyber attacks.\n\nA malware is considered active if it is found running on the machine or it already has persistence mechanisms in place. Active malware detections are assigned higher severity ratings.\n\nBecause this malware was active, take precautionary measures and check for residual signs of infection.","determination":null,"entities":{"accountName":"","entityType":"User"},"firstActivity":"2020-06-30T10:09:10.8889583Z","investigationState":"UnsupportedAlertType","serviceSource":"MicrosoftDefenderATP","status":"Resolved","title":"Suspicious 'AccessibilityEscalation' behavior was detected","classification":"FalsePositive","devices":[{"aadDeviceId":null,"healthStatus":"Inactive","osPlatform":"Other","osProcessor":"x64","riskScore":"High","deviceDnsName":"testserver4","firstSeen":"2020-06-30T08:55:08.8320449Z","mdatpDeviceId":"75a63a39f9bc5a964f417c11f6277d5bf9489f0d","osBuild":17763,"rbacGroupId":0,"rbacGroupName":null,"version":"Other"}],"mitreTechniques":[],"severity":"Low","threatFamilyName":null,"creationTime":"2020-06-30T10:10:16.1355657Z","detectionSource":"WindowsDefenderAv","incidentId":12,"alertId":"da637291086161511365_-2075772905","assignedTo":"elastic@elasticuser.com","category":"SuspiciousActivity","investigationId":null,"resolvedTime":"2020-09-23T19:44:36.1092821Z"},"assignedTo":"elastic@elasticuser.com","status":"Resolved","tags":[]} +{"determination":"NotAvailable","lastUpdateTime":"2020-09-23T19:44:36.29Z","tags":[],"alerts":{"investigationState":"UnsupportedAlertType","status":"Resolved","alertId":"da637291086161511365_-2075772905","assignedTo":"elastic@elasticuser.com","determination":null,"firstActivity":"2020-06-30T10:09:10.8889583Z","mitreTechniques":[],"resolvedTime":"2020-09-23T19:44:36.1092821Z","severity":"Low","actorName":null,"category":"SuspiciousActivity","description":"Malware and unwanted software are undesirable applications that perform annoying, disruptive, or harmful actions on affected machines. Some of these undesirable applications can replicate and spread from one machine to another. Others are able to receive commands from remote attackers and perform activities associated with cyber attacks.\n\nA malware is considered active if it is found running on the machine or it already has persistence mechanisms in place. Active malware detections are assigned higher severity ratings.\n\nBecause this malware was active, take precautionary measures and check for residual signs of infection.","lastUpdatedTime":"2020-09-23T19:44:37.9666667Z","title":"Suspicious 'AccessibilityEscalation' behavior was detected","classification":"FalsePositive","creationTime":"2020-06-30T10:10:16.1355657Z","entities":{"deviceId":"75a63a39f9bc5a964f417c11f6277d5bf9489f0d","entityType":"Process","processCreationTime":"2020-06-30T10:09:10.5747992Z","processId":1324},"incidentId":12,"serviceSource":"MicrosoftDefenderATP","threatFamilyName":null,"detectionSource":"WindowsDefenderAv","devices":[{"osPlatform":"Other","osProcessor":"x64","rbacGroupId":0,"riskScore":"High","version":"Other","aadDeviceId":null,"deviceDnsName":"testserver4","mdatpDeviceId":"75a63a39f9bc5a964f417c11f6277d5bf9489f0d","rbacGroupName":null,"firstSeen":"2020-06-30T08:55:08.8320449Z","healthStatus":"Inactive","osBuild":17763}],"investigationId":null,"lastActivity":"2020-06-30T10:31:09.4165785Z"},"assignedTo":"elastic@elasticuser.com","classification":"Unknown","createdTime":"2020-06-30T09:32:31.85Z","status":"Resolved","incidentId":12,"incidentName":"12","redirectIncidentId":null,"severity":"Low"} +{"incidentId":14,"incidentName":"Activity from infrequent country","redirectIncidentId":null,"tags":[],"alerts":{"category":"SuspiciousActivity","entities":{"aadUserId":"8e24c50a-a77c-4782-813f-965009b5ddf3","accountName":"brent","entityType":"User","userPrincipalName":"brent@elasticbv.onmicrosoft.com"},"incidentId":14,"investigationState":"UnsupportedAlertType","status":"New","actorName":null,"classification":"FalsePositive","description":"Brent Murphy (brent@elasticbv.onmicrosoft.com) performed an activity. No activity was performed in United States in the past 41 days.","investigationId":null,"lastActivity":"2020-07-27T15:47:22.088Z","lastUpdatedTime":"2020-09-23T19:32:17.5433333Z","mitreTechniques":[],"serviceSource":"MicrosoftCloudAppSecurity","severity":"Medium","threatFamilyName":null,"title":"Activity from infrequent country","assignedTo":"elastic@elasticuser.com","detectionSource":"MCAS","devices":[],"alertId":"caA214771F-6AB0-311D-B2B0-BECD3B4A967B","creationTime":"2020-07-27T15:54:20.52207Z","determination":null,"firstActivity":"2020-07-27T15:47:22.088Z","resolvedTime":null},"classification":"Unknown","determination":"NotAvailable","lastUpdateTime":"2020-09-23T19:32:05.8366667Z","severity":"Medium","status":"Active","assignedTo":"elastic@elasticuser.com","createdTime":"2020-07-27T15:54:21.58Z"} +{"incidentId":14,"incidentName":"Activity from infrequent country","severity":"Medium","status":"Active","tags":[],"alerts":{"description":"Brent Murphy (brent@elasticbv.onmicrosoft.com) performed an activity. No activity was performed in United States in the past 41 days.","detectionSource":"MCAS","firstActivity":"2020-07-27T15:47:22.088Z","investigationId":null,"investigationState":"UnsupportedAlertType","severity":"Medium","alertId":"caA214771F-6AB0-311D-B2B0-BECD3B4A967B","category":"SuspiciousActivity","classification":"FalsePositive","determination":null,"entities":{"entityType":"Ip","ipAddress":"73.172.171.53"},"incidentId":14,"serviceSource":"MicrosoftCloudAppSecurity","status":"New","actorName":null,"title":"Activity from infrequent country","devices":[],"lastActivity":"2020-07-27T15:47:22.088Z","lastUpdatedTime":"2020-09-23T19:32:17.5433333Z","creationTime":"2020-07-27T15:54:20.52207Z","mitreTechniques":[],"resolvedTime":null,"threatFamilyName":null,"assignedTo":"elastic@elasticuser.com"},"createdTime":"2020-07-27T15:54:21.58Z","determination":"NotAvailable","lastUpdateTime":"2020-09-23T19:32:05.8366667Z","redirectIncidentId":null,"assignedTo":"elastic@elasticuser.com","classification":"Unknown"} diff --git a/x-pack/filebeat/module/microsoft/m365_defender/test/m365_defender-test.ndjson.log-expected.json b/x-pack/filebeat/module/microsoft/m365_defender/test/m365_defender-test.ndjson.log-expected.json new file mode 100644 index 00000000000..1f81a57a98f --- /dev/null +++ b/x-pack/filebeat/module/microsoft/m365_defender/test/m365_defender-test.ndjson.log-expected.json @@ -0,0 +1,611 @@ +[ + { + "@timestamp": "2020-09-23T19:44:36.29Z", + "cloud.provider": "azure", + "event.action": "Malware", + "event.category": [ + "host" + ], + "event.dataset": "microsoft.m365_defender", + "event.duration": 892514711800, + "event.end": "2020-06-30T09:46:15.0876676Z", + "event.id": "da637291063515066999_-2102938302", + "event.kind": "alert", + "event.module": "microsoft", + "event.provider": "MicrosoftDefenderATP", + "event.severity": 2, + "event.start": "2020-06-30T09:31:22.5729558Z", + "event.timezone": "UTC", + "event.type": [ + "end" + ], + "file.hash.sha1": "ffb1670c6c6a9c5b4c5cea8b6b8e68d62e7ff281", + "file.hash.sha256": "fd46705c4f67a8ef16e76259ca6d6253241e51a1f8952223145f92aa1907d356", + "file.name": "amsistream-1D89ECED25A52AB98B76FF619B7BA07A", + "fileset.name": "m365_defender", + "input.type": "log", + "log.offset": 0, + "message": "'Mountsi' malware was detected", + "microsoft.m365_defender.alerts.assignedTo": "Automation", + "microsoft.m365_defender.alerts.creationTime": "2020-06-30T09:32:31.4579225Z", + "microsoft.m365_defender.alerts.detectionSource": "WindowsDefenderAv", + "microsoft.m365_defender.alerts.devices": [ + { + "deviceDnsName": "TestServer4", + "firstSeen": "2020-06-30T08:55:08.8320449Z", + "healthStatus": "Inactive", + "mdatpDeviceId": "75a63a39f9bc5a964f417c11f6277d5bf9489f0d", + "osBuild": 17763, + "osPlatform": "Other", + "osProcessor": "x64", + "rbacGroupId": 0, + "riskScore": "High", + "version": "Other" + } + ], + "microsoft.m365_defender.alerts.entities.deviceId": "75a63a39f9bc5a964f417c11f6277d5bf9489f0d", + "microsoft.m365_defender.alerts.entities.entityType": "File", + "microsoft.m365_defender.alerts.incidentId": "12", + "microsoft.m365_defender.alerts.investigationId": 9, + "microsoft.m365_defender.alerts.investigationState": "Benign", + "microsoft.m365_defender.alerts.lastUpdatedTime": "2020-08-26T09:41:27.7233333Z", + "microsoft.m365_defender.alerts.resolvedTime": "2020-06-30T11:13:12.2680434Z", + "microsoft.m365_defender.alerts.severity": "Informational", + "microsoft.m365_defender.alerts.status": "Resolved", + "microsoft.m365_defender.assignedTo": "elastic@elasticuser.com", + "microsoft.m365_defender.classification": "Unknown", + "microsoft.m365_defender.determination": "NotAvailable", + "microsoft.m365_defender.incidentId": "12", + "microsoft.m365_defender.incidentName": "12", + "microsoft.m365_defender.status": "Resolved", + "observer.name": "MicrosoftDefenderATP", + "observer.product": "365 Defender", + "observer.vendor": "Microsoft", + "related.hash": [ + "ffb1670c6c6a9c5b4c5cea8b6b8e68d62e7ff281", + "fd46705c4f67a8ef16e76259ca6d6253241e51a1f8952223145f92aa1907d356" + ], + "rule.description": "Malware and unwanted software are undesirable applications that perform annoying, disruptive, or harmful actions on affected machines. Some of these undesirable applications can replicate and spread from one machine to another. Others are able to receive commands from remote attackers and perform activities associated with cyber attacks.\n\nThis detection might indicate that the malware was stopped from delivering its payload. However, it is prudent to check the machine for signs of infection.", + "service.type": "microsoft", + "tags": [ + "m365-defender", + "forwarded" + ], + "threat.framework": "MITRE ATT&CK", + "threat.technique.name": "Malware" + }, + { + "@timestamp": "2020-09-23T19:44:36.29Z", + "cloud.provider": "azure", + "event.action": "Malware", + "event.category": [ + "host" + ], + "event.dataset": "microsoft.m365_defender", + "event.duration": 892514711800, + "event.end": "2020-06-30T09:46:15.0876676Z", + "event.id": "da637291063515066999_-2102938302", + "event.kind": "alert", + "event.module": "microsoft", + "event.provider": "MicrosoftDefenderATP", + "event.severity": 2, + "event.start": "2020-06-30T09:31:22.5729558Z", + "event.timezone": "UTC", + "event.type": [ + "end" + ], + "file.hash.sha1": "ffb1670c6c6a9c5b4c5cea8b6b8e68d62e7ff281", + "file.hash.sha256": "fd46705c4f67a8ef16e76259ca6d6253241e51a1f8952223145f92aa1907d356", + "file.name": "amsistream-B103C1A335BDB049E617D1AC4A41FCDC", + "fileset.name": "m365_defender", + "input.type": "log", + "log.offset": 2071, + "message": "'Mountsi' malware was detected", + "microsoft.m365_defender.alerts.assignedTo": "Automation", + "microsoft.m365_defender.alerts.creationTime": "2020-06-30T09:32:31.4579225Z", + "microsoft.m365_defender.alerts.detectionSource": "WindowsDefenderAv", + "microsoft.m365_defender.alerts.devices": [ + { + "deviceDnsName": "TestServer4", + "firstSeen": "2020-06-30T08:55:08.8320449Z", + "healthStatus": "Inactive", + "mdatpDeviceId": "75a63a39f9bc5a964f417c11f6277d5bf9489f0d", + "osBuild": 17763, + "osPlatform": "Other", + "osProcessor": "x64", + "rbacGroupId": 0, + "riskScore": "High", + "version": "Other" + } + ], + "microsoft.m365_defender.alerts.entities.deviceId": "75a63a39f9bc5a964f417c11f6277d5bf9489f0d", + "microsoft.m365_defender.alerts.entities.entityType": "File", + "microsoft.m365_defender.alerts.incidentId": "12", + "microsoft.m365_defender.alerts.investigationId": 9, + "microsoft.m365_defender.alerts.investigationState": "Benign", + "microsoft.m365_defender.alerts.lastUpdatedTime": "2020-08-26T09:41:27.7233333Z", + "microsoft.m365_defender.alerts.resolvedTime": "2020-06-30T11:13:12.2680434Z", + "microsoft.m365_defender.alerts.severity": "Informational", + "microsoft.m365_defender.alerts.status": "Resolved", + "microsoft.m365_defender.assignedTo": "elastic@elasticuser.com", + "microsoft.m365_defender.classification": "Unknown", + "microsoft.m365_defender.determination": "NotAvailable", + "microsoft.m365_defender.incidentId": "12", + "microsoft.m365_defender.incidentName": "12", + "microsoft.m365_defender.status": "Resolved", + "observer.name": "MicrosoftDefenderATP", + "observer.product": "365 Defender", + "observer.vendor": "Microsoft", + "related.hash": [ + "ffb1670c6c6a9c5b4c5cea8b6b8e68d62e7ff281", + "fd46705c4f67a8ef16e76259ca6d6253241e51a1f8952223145f92aa1907d356" + ], + "rule.description": "Malware and unwanted software are undesirable applications that perform annoying, disruptive, or harmful actions on affected machines. Some of these undesirable applications can replicate and spread from one machine to another. Others are able to receive commands from remote attackers and perform activities associated with cyber attacks.\n\nThis detection might indicate that the malware was stopped from delivering its payload. However, it is prudent to check the machine for signs of infection.", + "service.type": "microsoft", + "tags": [ + "m365-defender", + "forwarded" + ], + "threat.framework": "MITRE ATT&CK", + "threat.technique.name": "Malware" + }, + { + "@timestamp": "2020-09-23T19:44:36.29Z", + "cloud.provider": "azure", + "event.action": "Malware", + "event.category": [ + "host" + ], + "event.dataset": "microsoft.m365_defender", + "event.duration": 0, + "event.end": "2020-06-30T10:07:44.3144099Z", + "event.id": "da637291085389812387_-1296910232", + "event.kind": "alert", + "event.module": "microsoft", + "event.provider": "MicrosoftDefenderATP", + "event.severity": 2, + "event.start": "2020-06-30T10:07:44.3144099Z", + "event.timezone": "UTC", + "event.type": [ + "end" + ], + "file.hash.sha1": "d1bb29ce3d01d01451e3623132545d5f577a1bd6", + "file.hash.sha256": "ce8d3a3811a3bf923902d6924532308506fe5d024435ddee0cabf90ad9b29f6a", + "file.name": "SB.xsl", + "file.path": "C:\\Windows\\Temp\\sb-sim-temp-ikyxqi\\sb_10554_bs_h4qpk5", + "fileset.name": "m365_defender", + "input.type": "log", + "log.offset": 4142, + "message": "'Exeselrun' malware was detected", + "microsoft.m365_defender.alerts.assignedTo": "Automation", + "microsoft.m365_defender.alerts.creationTime": "2020-06-30T10:08:58.9655663Z", + "microsoft.m365_defender.alerts.detectionSource": "WindowsDefenderAv", + "microsoft.m365_defender.alerts.devices": [ + { + "deviceDnsName": "testserver4", + "firstSeen": "2020-06-30T08:55:08.8320449Z", + "healthStatus": "Inactive", + "mdatpDeviceId": "75a63a39f9bc5a964f417c11f6277d5bf9489f0d", + "osBuild": 17763, + "osPlatform": "Other", + "osProcessor": "x64", + "rbacGroupId": 0, + "riskScore": "High", + "version": "Other" + } + ], + "microsoft.m365_defender.alerts.entities.deviceId": "75a63a39f9bc5a964f417c11f6277d5bf9489f0d", + "microsoft.m365_defender.alerts.entities.entityType": "File", + "microsoft.m365_defender.alerts.incidentId": "12", + "microsoft.m365_defender.alerts.investigationId": 9, + "microsoft.m365_defender.alerts.investigationState": "Benign", + "microsoft.m365_defender.alerts.lastUpdatedTime": "2020-08-26T09:41:27.7233333Z", + "microsoft.m365_defender.alerts.resolvedTime": "2020-06-30T11:13:12.2680434Z", + "microsoft.m365_defender.alerts.severity": "Informational", + "microsoft.m365_defender.alerts.status": "Resolved", + "microsoft.m365_defender.assignedTo": "elastic@elasticuser.com", + "microsoft.m365_defender.classification": "Unknown", + "microsoft.m365_defender.determination": "NotAvailable", + "microsoft.m365_defender.incidentId": "12", + "microsoft.m365_defender.incidentName": "12", + "microsoft.m365_defender.status": "Resolved", + "observer.name": "MicrosoftDefenderATP", + "observer.product": "365 Defender", + "observer.vendor": "Microsoft", + "related.hash": [ + "d1bb29ce3d01d01451e3623132545d5f577a1bd6", + "ce8d3a3811a3bf923902d6924532308506fe5d024435ddee0cabf90ad9b29f6a" + ], + "rule.description": "Malware and unwanted software are undesirable applications that perform annoying, disruptive, or harmful actions on affected machines. Some of these undesirable applications can replicate and spread from one machine to another. Others are able to receive commands from remote attackers and perform activities associated with cyber attacks.\n\nThis detection might indicate that the malware was stopped from delivering its payload. However, it is prudent to check the machine for signs of infection.", + "service.type": "microsoft", + "tags": [ + "m365-defender", + "forwarded" + ], + "threat.framework": "MITRE ATT&CK", + "threat.technique.name": "Malware" + }, + { + "@timestamp": "2020-09-23T19:44:36.29Z", + "cloud.provider": "azure", + "event.action": "Malware", + "event.category": [ + "host" + ], + "event.dataset": "microsoft.m365_defender", + "event.duration": 0, + "event.end": "2020-06-30T10:07:44.333733Z", + "event.id": "da637291085411733957_-1043898914", + "event.kind": "alert", + "event.module": "microsoft", + "event.provider": "MicrosoftDefenderATP", + "event.severity": 2, + "event.start": "2020-06-30T10:07:44.333733Z", + "event.timezone": "UTC", + "event.type": [ + "end" + ], + "file.name": "SB.xsl", + "file.path": "C:\\Windows\\Temp\\sb-sim-temp-ikyxqi\\sb_10554_bs_h4qpk5", + "fileset.name": "m365_defender", + "input.type": "log", + "log.offset": 6249, + "message": "An active 'Exeselrun' malware was detected", + "microsoft.m365_defender.alerts.assignedTo": "elastic@elasticuser.com", + "microsoft.m365_defender.alerts.creationTime": "2020-06-30T10:09:01.1569718Z", + "microsoft.m365_defender.alerts.detectionSource": "WindowsDefenderAv", + "microsoft.m365_defender.alerts.devices": [ + { + "deviceDnsName": "TestServer4", + "firstSeen": "2020-06-30T08:55:08.8320449Z", + "healthStatus": "Inactive", + "mdatpDeviceId": "75a63a39f9bc5a964f417c11f6277d5bf9489f0d", + "osBuild": 17763, + "osPlatform": "Other", + "osProcessor": "x64", + "rbacGroupId": 0, + "riskScore": "High", + "version": "Other" + } + ], + "microsoft.m365_defender.alerts.entities.deviceId": "75a63a39f9bc5a964f417c11f6277d5bf9489f0d", + "microsoft.m365_defender.alerts.entities.entityType": "File", + "microsoft.m365_defender.alerts.incidentId": "12", + "microsoft.m365_defender.alerts.investigationId": 9, + "microsoft.m365_defender.alerts.investigationState": "Benign", + "microsoft.m365_defender.alerts.lastUpdatedTime": "2020-08-26T09:41:27.7233333Z", + "microsoft.m365_defender.alerts.resolvedTime": "2020-06-30T11:13:12.2680434Z", + "microsoft.m365_defender.alerts.severity": "Low", + "microsoft.m365_defender.alerts.status": "Resolved", + "microsoft.m365_defender.assignedTo": "elastic@elasticuser.com", + "microsoft.m365_defender.classification": "Unknown", + "microsoft.m365_defender.determination": "NotAvailable", + "microsoft.m365_defender.incidentId": "12", + "microsoft.m365_defender.incidentName": "12", + "microsoft.m365_defender.status": "Resolved", + "observer.name": "MicrosoftDefenderATP", + "observer.product": "365 Defender", + "observer.vendor": "Microsoft", + "rule.description": "Malware and unwanted software are undesirable applications that perform annoying, disruptive, or harmful actions on affected machines. Some of these undesirable applications can replicate and spread from one machine to another. Others are able to receive commands from remote attackers and perform activities associated with cyber attacks.\n\nA malware is considered active if it is found running on the machine or it already has persistence mechanisms in place. Active malware detections are assigned higher severity ratings.\n\nBecause this malware was active, take precautionary measures and check for residual signs of infection.", + "service.type": "microsoft", + "tags": [ + "m365-defender", + "forwarded" + ], + "threat.framework": "MITRE ATT&CK", + "threat.technique.name": "Malware" + }, + { + "@timestamp": "2020-09-23T19:44:36.29Z", + "cloud.provider": "azure", + "event.action": "SuspiciousActivity", + "event.category": [ + "host" + ], + "event.dataset": "microsoft.m365_defender", + "event.duration": 1318527620200, + "event.end": "2020-06-30T10:31:09.4165785Z", + "event.id": "da637291086161511365_-2075772905", + "event.kind": "alert", + "event.module": "microsoft", + "event.provider": "MicrosoftDefenderATP", + "event.severity": 2, + "event.start": "2020-06-30T10:09:10.8889583Z", + "event.timezone": "UTC", + "event.type": [ + "end" + ], + "fileset.name": "m365_defender", + "input.type": "log", + "log.offset": 8376, + "message": "Suspicious 'AccessibilityEscalation' behavior was detected", + "microsoft.m365_defender.alerts.assignedTo": "elastic@elasticuser.com", + "microsoft.m365_defender.alerts.classification": "FalsePositive", + "microsoft.m365_defender.alerts.creationTime": "2020-06-30T10:10:16.1355657Z", + "microsoft.m365_defender.alerts.detectionSource": "WindowsDefenderAv", + "microsoft.m365_defender.alerts.devices": [ + { + "deviceDnsName": "testserver4", + "firstSeen": "2020-06-30T08:55:08.8320449Z", + "healthStatus": "Inactive", + "mdatpDeviceId": "75a63a39f9bc5a964f417c11f6277d5bf9489f0d", + "osBuild": 17763, + "osPlatform": "Other", + "osProcessor": "x64", + "rbacGroupId": 0, + "riskScore": "High", + "version": "Other" + } + ], + "microsoft.m365_defender.alerts.entities.deviceId": "75a63a39f9bc5a964f417c11f6277d5bf9489f0d", + "microsoft.m365_defender.alerts.entities.entityType": "Process", + "microsoft.m365_defender.alerts.incidentId": "12", + "microsoft.m365_defender.alerts.investigationState": "UnsupportedAlertType", + "microsoft.m365_defender.alerts.lastUpdatedTime": "2020-09-23T19:44:37.9666667Z", + "microsoft.m365_defender.alerts.resolvedTime": "2020-09-23T19:44:36.1092821Z", + "microsoft.m365_defender.alerts.severity": "Low", + "microsoft.m365_defender.alerts.status": "Resolved", + "microsoft.m365_defender.assignedTo": "elastic@elasticuser.com", + "microsoft.m365_defender.classification": "Unknown", + "microsoft.m365_defender.determination": "NotAvailable", + "microsoft.m365_defender.incidentId": "12", + "microsoft.m365_defender.incidentName": "12", + "microsoft.m365_defender.status": "Resolved", + "observer.name": "MicrosoftDefenderATP", + "observer.product": "365 Defender", + "observer.vendor": "Microsoft", + "process.pid": 6720, + "process.start": "2020-06-30T10:31:04.1092404Z", + "rule.description": "Malware and unwanted software are undesirable applications that perform annoying, disruptive, or harmful actions on affected machines. Some of these undesirable applications can replicate and spread from one machine to another. Others are able to receive commands from remote attackers and perform activities associated with cyber attacks.\n\nA malware is considered active if it is found running on the machine or it already has persistence mechanisms in place. Active malware detections are assigned higher severity ratings.\n\nBecause this malware was active, take precautionary measures and check for residual signs of infection.", + "service.type": "microsoft", + "tags": [ + "m365-defender", + "forwarded" + ], + "threat.framework": "MITRE ATT&CK", + "threat.technique.name": "SuspiciousActivity" + }, + { + "@timestamp": "2020-09-23T19:44:36.29Z", + "cloud.provider": "azure", + "event.action": "SuspiciousActivity", + "event.category": [ + "host" + ], + "event.dataset": "microsoft.m365_defender", + "event.duration": 1318527620200, + "event.end": "2020-06-30T10:31:09.4165785Z", + "event.id": "da637291086161511365_-2075772905", + "event.kind": "alert", + "event.module": "microsoft", + "event.provider": "MicrosoftDefenderATP", + "event.severity": 2, + "event.start": "2020-06-30T10:09:10.8889583Z", + "event.timezone": "UTC", + "event.type": [ + "end" + ], + "fileset.name": "m365_defender", + "input.type": "log", + "log.offset": 10542, + "message": "Suspicious 'AccessibilityEscalation' behavior was detected", + "microsoft.m365_defender.alerts.assignedTo": "elastic@elasticuser.com", + "microsoft.m365_defender.alerts.classification": "FalsePositive", + "microsoft.m365_defender.alerts.creationTime": "2020-06-30T10:10:16.1355657Z", + "microsoft.m365_defender.alerts.detectionSource": "WindowsDefenderAv", + "microsoft.m365_defender.alerts.devices": [ + { + "deviceDnsName": "testserver4", + "firstSeen": "2020-06-30T08:55:08.8320449Z", + "healthStatus": "Inactive", + "mdatpDeviceId": "75a63a39f9bc5a964f417c11f6277d5bf9489f0d", + "osBuild": 17763, + "osPlatform": "Other", + "osProcessor": "x64", + "rbacGroupId": 0, + "riskScore": "High", + "version": "Other" + } + ], + "microsoft.m365_defender.alerts.entities.accountName": "", + "microsoft.m365_defender.alerts.entities.entityType": "User", + "microsoft.m365_defender.alerts.incidentId": "12", + "microsoft.m365_defender.alerts.investigationState": "UnsupportedAlertType", + "microsoft.m365_defender.alerts.lastUpdatedTime": "2020-09-23T19:44:37.9666667Z", + "microsoft.m365_defender.alerts.resolvedTime": "2020-09-23T19:44:36.1092821Z", + "microsoft.m365_defender.alerts.severity": "Low", + "microsoft.m365_defender.alerts.status": "Resolved", + "microsoft.m365_defender.assignedTo": "elastic@elasticuser.com", + "microsoft.m365_defender.classification": "Unknown", + "microsoft.m365_defender.determination": "NotAvailable", + "microsoft.m365_defender.incidentId": "12", + "microsoft.m365_defender.incidentName": "12", + "microsoft.m365_defender.status": "Resolved", + "observer.name": "MicrosoftDefenderATP", + "observer.product": "365 Defender", + "observer.vendor": "Microsoft", + "rule.description": "Malware and unwanted software are undesirable applications that perform annoying, disruptive, or harmful actions on affected machines. Some of these undesirable applications can replicate and spread from one machine to another. Others are able to receive commands from remote attackers and perform activities associated with cyber attacks.\n\nA malware is considered active if it is found running on the machine or it already has persistence mechanisms in place. Active malware detections are assigned higher severity ratings.\n\nBecause this malware was active, take precautionary measures and check for residual signs of infection.", + "service.type": "microsoft", + "tags": [ + "m365-defender", + "forwarded" + ], + "threat.framework": "MITRE ATT&CK", + "threat.technique.name": "SuspiciousActivity" + }, + { + "@timestamp": "2020-09-23T19:44:36.29Z", + "cloud.provider": "azure", + "event.action": "SuspiciousActivity", + "event.category": [ + "host" + ], + "event.dataset": "microsoft.m365_defender", + "event.duration": 1318527620200, + "event.end": "2020-06-30T10:31:09.4165785Z", + "event.id": "da637291086161511365_-2075772905", + "event.kind": "alert", + "event.module": "microsoft", + "event.provider": "MicrosoftDefenderATP", + "event.severity": 2, + "event.start": "2020-06-30T10:09:10.8889583Z", + "event.timezone": "UTC", + "event.type": [ + "end" + ], + "fileset.name": "m365_defender", + "input.type": "log", + "log.offset": 12598, + "message": "Suspicious 'AccessibilityEscalation' behavior was detected", + "microsoft.m365_defender.alerts.assignedTo": "elastic@elasticuser.com", + "microsoft.m365_defender.alerts.classification": "FalsePositive", + "microsoft.m365_defender.alerts.creationTime": "2020-06-30T10:10:16.1355657Z", + "microsoft.m365_defender.alerts.detectionSource": "WindowsDefenderAv", + "microsoft.m365_defender.alerts.devices": [ + { + "deviceDnsName": "testserver4", + "firstSeen": "2020-06-30T08:55:08.8320449Z", + "healthStatus": "Inactive", + "mdatpDeviceId": "75a63a39f9bc5a964f417c11f6277d5bf9489f0d", + "osBuild": 17763, + "osPlatform": "Other", + "osProcessor": "x64", + "rbacGroupId": 0, + "riskScore": "High", + "version": "Other" + } + ], + "microsoft.m365_defender.alerts.entities.deviceId": "75a63a39f9bc5a964f417c11f6277d5bf9489f0d", + "microsoft.m365_defender.alerts.entities.entityType": "Process", + "microsoft.m365_defender.alerts.incidentId": "12", + "microsoft.m365_defender.alerts.investigationState": "UnsupportedAlertType", + "microsoft.m365_defender.alerts.lastUpdatedTime": "2020-09-23T19:44:37.9666667Z", + "microsoft.m365_defender.alerts.resolvedTime": "2020-09-23T19:44:36.1092821Z", + "microsoft.m365_defender.alerts.severity": "Low", + "microsoft.m365_defender.alerts.status": "Resolved", + "microsoft.m365_defender.assignedTo": "elastic@elasticuser.com", + "microsoft.m365_defender.classification": "Unknown", + "microsoft.m365_defender.determination": "NotAvailable", + "microsoft.m365_defender.incidentId": "12", + "microsoft.m365_defender.incidentName": "12", + "microsoft.m365_defender.status": "Resolved", + "observer.name": "MicrosoftDefenderATP", + "observer.product": "365 Defender", + "observer.vendor": "Microsoft", + "process.pid": 1324, + "process.start": "2020-06-30T10:09:10.5747992Z", + "rule.description": "Malware and unwanted software are undesirable applications that perform annoying, disruptive, or harmful actions on affected machines. Some of these undesirable applications can replicate and spread from one machine to another. Others are able to receive commands from remote attackers and perform activities associated with cyber attacks.\n\nA malware is considered active if it is found running on the machine or it already has persistence mechanisms in place. Active malware detections are assigned higher severity ratings.\n\nBecause this malware was active, take precautionary measures and check for residual signs of infection.", + "service.type": "microsoft", + "tags": [ + "m365-defender", + "forwarded" + ], + "threat.framework": "MITRE ATT&CK", + "threat.technique.name": "SuspiciousActivity" + }, + { + "@timestamp": "2020-09-23T19:32:05.8366667Z", + "cloud.provider": "azure", + "event.action": "SuspiciousActivity", + "event.category": [ + "host" + ], + "event.dataset": "microsoft.m365_defender", + "event.duration": 0, + "event.end": "2020-07-27T15:47:22.088Z", + "event.id": "caA214771F-6AB0-311D-B2B0-BECD3B4A967B", + "event.kind": "alert", + "event.module": "microsoft", + "event.provider": "MicrosoftCloudAppSecurity", + "event.severity": 3, + "event.start": "2020-07-27T15:47:22.088Z", + "event.timezone": "UTC", + "fileset.name": "m365_defender", + "host.user.id": "8e24c50a-a77c-4782-813f-965009b5ddf3", + "host.user.name": "brent@elasticbv.onmicrosoft.com", + "input.type": "log", + "log.offset": 14764, + "message": "Activity from infrequent country", + "microsoft.m365_defender.alerts.assignedTo": "elastic@elasticuser.com", + "microsoft.m365_defender.alerts.classification": "FalsePositive", + "microsoft.m365_defender.alerts.creationTime": "2020-07-27T15:54:20.52207Z", + "microsoft.m365_defender.alerts.detectionSource": "MCAS", + "microsoft.m365_defender.alerts.entities.accountName": "brent", + "microsoft.m365_defender.alerts.entities.entityType": "User", + "microsoft.m365_defender.alerts.incidentId": "14", + "microsoft.m365_defender.alerts.investigationState": "UnsupportedAlertType", + "microsoft.m365_defender.alerts.lastUpdatedTime": "2020-09-23T19:32:17.5433333Z", + "microsoft.m365_defender.alerts.severity": "Medium", + "microsoft.m365_defender.alerts.status": "New", + "microsoft.m365_defender.assignedTo": "elastic@elasticuser.com", + "microsoft.m365_defender.classification": "Unknown", + "microsoft.m365_defender.determination": "NotAvailable", + "microsoft.m365_defender.incidentId": "14", + "microsoft.m365_defender.incidentName": "Activity from infrequent country", + "microsoft.m365_defender.status": "Active", + "observer.name": "MicrosoftCloudAppSecurity", + "observer.product": "365 Defender", + "observer.vendor": "Microsoft", + "related.user": [ + "brent@elasticbv.onmicrosoft.com" + ], + "rule.description": "Brent Murphy (brent@elasticbv.onmicrosoft.com) performed an activity. No activity was performed in United States in the past 41 days.", + "service.type": "microsoft", + "tags": [ + "m365-defender", + "forwarded" + ], + "threat.framework": "MITRE ATT&CK", + "threat.technique.name": "SuspiciousActivity" + }, + { + "@timestamp": "2020-09-23T19:32:05.8366667Z", + "cloud.provider": "azure", + "event.action": "SuspiciousActivity", + "event.category": [ + "host" + ], + "event.dataset": "microsoft.m365_defender", + "event.duration": 0, + "event.end": "2020-07-27T15:47:22.088Z", + "event.id": "caA214771F-6AB0-311D-B2B0-BECD3B4A967B", + "event.kind": "alert", + "event.module": "microsoft", + "event.provider": "MicrosoftCloudAppSecurity", + "event.severity": 3, + "event.start": "2020-07-27T15:47:22.088Z", + "event.timezone": "UTC", + "fileset.name": "m365_defender", + "input.type": "log", + "log.offset": 16091, + "message": "Activity from infrequent country", + "microsoft.m365_defender.alerts.assignedTo": "elastic@elasticuser.com", + "microsoft.m365_defender.alerts.classification": "FalsePositive", + "microsoft.m365_defender.alerts.creationTime": "2020-07-27T15:54:20.52207Z", + "microsoft.m365_defender.alerts.detectionSource": "MCAS", + "microsoft.m365_defender.alerts.entities.entityType": "Ip", + "microsoft.m365_defender.alerts.entities.ipAddress": "73.172.171.53", + "microsoft.m365_defender.alerts.incidentId": "14", + "microsoft.m365_defender.alerts.investigationState": "UnsupportedAlertType", + "microsoft.m365_defender.alerts.lastUpdatedTime": "2020-09-23T19:32:17.5433333Z", + "microsoft.m365_defender.alerts.severity": "Medium", + "microsoft.m365_defender.alerts.status": "New", + "microsoft.m365_defender.assignedTo": "elastic@elasticuser.com", + "microsoft.m365_defender.classification": "Unknown", + "microsoft.m365_defender.determination": "NotAvailable", + "microsoft.m365_defender.incidentId": "14", + "microsoft.m365_defender.incidentName": "Activity from infrequent country", + "microsoft.m365_defender.status": "Active", + "observer.name": "MicrosoftCloudAppSecurity", + "observer.product": "365 Defender", + "observer.vendor": "Microsoft", + "rule.description": "Brent Murphy (brent@elasticbv.onmicrosoft.com) performed an activity. No activity was performed in United States in the past 41 days.", + "service.type": "microsoft", + "tags": [ + "m365-defender", + "forwarded" + ], + "threat.framework": "MITRE ATT&CK", + "threat.technique.name": "SuspiciousActivity" + } +] \ No newline at end of file diff --git a/x-pack/filebeat/modules.d/microsoft.yml.disabled b/x-pack/filebeat/modules.d/microsoft.yml.disabled index 09c7211e179..63bcc20897a 100644 --- a/x-pack/filebeat/modules.d/microsoft.yml.disabled +++ b/x-pack/filebeat/modules.d/microsoft.yml.disabled @@ -13,7 +13,20 @@ # Oauth Client Secret #var.oauth2.client.secret: "" - + + # Oauth Token URL, should include the tenant ID + #var.oauth2.token_url: "https://login.microsoftonline.com/TENANT-ID/oauth2/token" + m365_defender: + enabled: true + # How often the API should be polled + #var.interval: 5m + + # Oauth Client ID + #var.oauth2.client.id: "" + + # Oauth Client Secret + #var.oauth2.client.secret: "" + # Oauth Token URL, should include the tenant ID #var.oauth2.token_url: "https://login.microsoftonline.com/TENANT-ID/oauth2/token" dhcp: From 0dd24289fa493ec742a54ad791cd7c3ed4bedf0f Mon Sep 17 00:00:00 2001 From: Marc Guasch Date: Tue, 6 Oct 2020 12:24:51 +0200 Subject: [PATCH 31/93] [Packetbeat] New SIP protocol (#21221) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * new protocol:sip:Make a directory and Readme file. new protocol:sip:update include list new protocol:sip:initial blank file DNSをベースとしてまずはDNSでやっている内容を解析開始・・・コメント付け中。 読み進めた分更新 パーサの実装を開始 パーサをのデコーダをsipPlugin内に移設もろもろ リクエスト、レスポンス判定を追加 SIPのパース, SDPのパース追加、パーサをいろいろばらばらに。醜い・・・ ヘッダパース系メソッドをsipMessageのメンバに変更 バッファリングの仕組みのプロトタイプ作成 ファイル分割、オブジェクト毎にファイルを分割。その他実装を進めているところ。とちゅう publish methodの実装 一部エラーハンドリングを追加 TODO更新とデータ構造を追加 ヘッダ処理諸々追加,途中 README TODO更新 ' 実働確認用に追加 フィールド名にsip.を付与、unixtimenanoをフィールドに追加(デフォルトのtimestampだとSIP信号を並び替えるのに精度不足なため。) フィールド命名規則を更新 Added english description change field name align the indents fields.yml update about timestamp テストケース追加 added testcase add testcases add testcases and fixed some bug cases add test cases add testcase parseSIPHeaders fixed bug cases. comment and refactoring update testcases add monitoring element named 'sip.message_ignored' move publish function call from expireBuffer to callback function when buffer expired. add testcase, bufferExpire remove unnecessary pkg add testcase at publish method add, edit and migrate test cases modify time duration change timer code remove fragmneted process translate comments add linux amd64 binary Comments translated update informations add windows bin update TODO list add no mandantory header parse check Add compact-form test case Add compact-form test case Add compact-form test case Add compact-form test case support compact form TODO list update add sip uri parser add detail mode add binary remove unnecessary file bug fix:broken when response parse in detail mode bug fix:detail mode modify detail mode modify detail mode Update Readme about compact form and parse detail sip header and request-uri Update Readme about compact form and parse detail sip header and request-uri Update Readme about compact form and parse detail sip header and request-uri Update Readme about compact form and parse detail sip header and request-uri expand config parsing options edit variable names arrangement and add text README file refine Readme message update Readme text update Readme on Configuration Go coding style was checked with golint Erase duplicate field in field.yml, move src and dst fields into sip filed Update docs, fields.asciidoc Update docs, fields.asciidoc * Fixes and style changes * Refactor to be more similar to http parser and add system tests * Add event action * Add related fields * Update fields and docs * Add sip to docs * Add beta warning * Parse SDP, Contact, Via and auth * Add suggestions Co-authored-by: tj8000rpm --- CHANGELOG.next.asciidoc | 1 + packetbeat/_meta/config/beat.docker.yml.tmpl | 3 + .../_meta/config/beat.reference.yml.tmpl | 13 + packetbeat/_meta/config/beat.yml.tmpl | 4 + packetbeat/_meta/sample_outputs/sip.json | 77 ++ packetbeat/docs/fields.asciidoc | 662 +++++++++++++++++ packetbeat/docs/shared-protocol-list.asciidoc | 1 + packetbeat/include/list.go | 1 + packetbeat/packetbeat.docker.yml | 3 + packetbeat/packetbeat.reference.yml | 13 + packetbeat/packetbeat.yml | 4 + packetbeat/pb/ecs.go | 6 +- packetbeat/pb/event.go | 47 +- packetbeat/protos/sip/README.md | 123 ++++ packetbeat/protos/sip/_meta/fields.yml | 238 +++++++ packetbeat/protos/sip/config.go | 41 ++ packetbeat/protos/sip/event.go | 102 +++ packetbeat/protos/sip/fields.go | 36 + packetbeat/protos/sip/parser.go | 469 ++++++++++++ packetbeat/protos/sip/plugin.go | 617 ++++++++++++++++ packetbeat/protos/sip/plugin_test.go | 168 +++++ .../tests/system/config/golden-tests.yml | 8 + .../tests/system/config/packetbeat.yml.j2 | 3 + .../tests/system/golden/sip-expected.json | 668 ++++++++++++++++++ .../sip_authenticated_register-expected.json | 142 ++++ packetbeat/tests/system/pcaps/sip.pcap | Bin 0 -> 6632 bytes .../pcaps/sip_authenticated_register.pcap | Bin 0 -> 1654 bytes .../tests/system/test_0099_golden_files.py | 3 + 28 files changed, 3451 insertions(+), 2 deletions(-) create mode 100644 packetbeat/_meta/sample_outputs/sip.json create mode 100644 packetbeat/protos/sip/README.md create mode 100644 packetbeat/protos/sip/_meta/fields.yml create mode 100644 packetbeat/protos/sip/config.go create mode 100644 packetbeat/protos/sip/event.go create mode 100644 packetbeat/protos/sip/fields.go create mode 100644 packetbeat/protos/sip/parser.go create mode 100644 packetbeat/protos/sip/plugin.go create mode 100644 packetbeat/protos/sip/plugin_test.go create mode 100644 packetbeat/tests/system/golden/sip-expected.json create mode 100644 packetbeat/tests/system/golden/sip_authenticated_register-expected.json create mode 100644 packetbeat/tests/system/pcaps/sip.pcap create mode 100644 packetbeat/tests/system/pcaps/sip_authenticated_register.pcap diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index c86fc85e347..15c2f9fe8a8 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -746,6 +746,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d port. {pull}19209[19209] - Add ECS fields for x509 certs, event categorization, and related IP info. {pull}19167[19167] - Add 100-continue support {issue}15830[15830] {pull}19349[19349] +- Add initial SIP protocol support {pull}21221[21221] *Functionbeat* diff --git a/packetbeat/_meta/config/beat.docker.yml.tmpl b/packetbeat/_meta/config/beat.docker.yml.tmpl index f4f0db1f7e6..f9b573edfeb 100644 --- a/packetbeat/_meta/config/beat.docker.yml.tmpl +++ b/packetbeat/_meta/config/beat.docker.yml.tmpl @@ -36,3 +36,6 @@ packetbeat.protocols.cassandra: packetbeat.protocols.tls: ports: [443, 993, 995, 5223, 8443, 8883, 9243] + +packetbeat.protocols.sip: + ports: [5060] diff --git a/packetbeat/_meta/config/beat.reference.yml.tmpl b/packetbeat/_meta/config/beat.reference.yml.tmpl index 1a3aab315d7..722c47102dc 100644 --- a/packetbeat/_meta/config/beat.reference.yml.tmpl +++ b/packetbeat/_meta/config/beat.reference.yml.tmpl @@ -531,6 +531,19 @@ packetbeat.protocols: # Set to true to publish fields with null values in events. #keep_null: false +- type: sip + # Configure the ports where to listen for SIP traffic. You can disable the SIP protocol by commenting out the list of ports. + ports: [5060] + + # Parse the authorization headers + parse_authorization: true + + # Parse body contents (only when body is SDP) + parse_body: true + + # Preserve original contents in event.original + keep_original: true + {{header "Monitored processes"}} # Packetbeat can enrich events with information about the process associated diff --git a/packetbeat/_meta/config/beat.yml.tmpl b/packetbeat/_meta/config/beat.yml.tmpl index fb221cba3c9..c5f9cfc0c23 100644 --- a/packetbeat/_meta/config/beat.yml.tmpl +++ b/packetbeat/_meta/config/beat.yml.tmpl @@ -101,6 +101,10 @@ packetbeat.protocols: - 8883 # Secure MQTT - 9243 # Elasticsearch +- type: sip + # Configure the ports where to listen for SIP traffic. You can disable the SIP protocol by commenting out the list of ports. + ports: [5060] + {{header "Elasticsearch template setting"}} setup.template.settings: diff --git a/packetbeat/_meta/sample_outputs/sip.json b/packetbeat/_meta/sample_outputs/sip.json new file mode 100644 index 00000000000..4a57d85908f --- /dev/null +++ b/packetbeat/_meta/sample_outputs/sip.json @@ -0,0 +1,77 @@ +{ + "@metadata.beat": "packetbeat", + "@metadata.type": "_doc", + "client.ip": "192.168.1.2", + "client.port": 5060, + "destination.ip": "212.242.33.35", + "destination.port": 5060, + "event.action": "sip_register", + "event.category": [ + "network", + "protocol", + "authentication" + ], + "event.dataset": "sip", + "event.duration": 0, + "event.kind": "event", + "event.original": "REGISTER sip:sip.cybercity.dk SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp112903503-43a64480192.168.1.2;rport\r\nFrom: ;tag=6bac55c\r\nTo: \r\nCall-ID: 578222729-4665d775@578222732-4665d772\r\nContact: ;expires=1200;q=0.500\r\nExpires: 1200\r\nCSeq: 75 REGISTER\r\nContent-Length: 0\r\nAuthorization: Digest username=\"voi18062\",realm=\"sip.cybercity.dk\",uri=\"sip:192.168.1.2\",nonce=\"1701b22972b90f440c3e4eb250842bb\",opaque=\"1701a1351f70795\",nc=\"00000001\",response=\"79a0543188495d288c9ebbe0c881abdc\"\r\nMax-Forwards: 70\r\nUser-Agent: Nero SIPPS IP Phone Version 2.0.51.16\r\n\r\n", + "event.sequence": 75, + "event.type": [ + "info" + ], + "network.application": "sip", + "network.community_id": "1:dOa61R2NaaJsJlcFAiMIiyXX+Kk=", + "network.iana_number": "17", + "network.protocol": "sip", + "network.transport": "udp", + "network.type": "ipv4", + "related.hosts": [ + "sip.cybercity.dk" + ], + "related.ip": [ + "192.168.1.2", + "212.242.33.35" + ], + "related.user": [ + "voi18062" + ], + "server.ip": "212.242.33.35", + "server.port": 5060, + "sip.auth.realm": "sip.cybercity.dk", + "sip.auth.scheme": "Digest", + "sip.auth.uri.host": "192.168.1.2", + "sip.auth.uri.original": "sip:192.168.1.2", + "sip.auth.uri.scheme": "sip", + "sip.call_id": "578222729-4665d775@578222732-4665d772", + "sip.contact.uri.host": "sip.cybercity.dk", + "sip.contact.uri.original": "sip:voi18062@sip.cybercity.dk", + "sip.contact.uri.scheme": "sip", + "sip.contact.uri.username": "voi18062", + "sip.cseq.code": 75, + "sip.cseq.method": "REGISTER", + "sip.from.tag": "6bac55c", + "sip.from.uri.host": "sip.cybercity.dk", + "sip.from.uri.original": "sip:voi18062@sip.cybercity.dk", + "sip.from.uri.scheme": "sip", + "sip.from.uri.username": "voi18062", + "sip.max_forwards": 70, + "sip.method": "REGISTER", + "sip.to.uri.host": "sip.cybercity.dk", + "sip.to.uri.original": "sip:voi18062@sip.cybercity.dk", + "sip.to.uri.scheme": "sip", + "sip.to.uri.username": "voi18062", + "sip.type": "request", + "sip.uri.host": "sip.cybercity.dk", + "sip.uri.original": "sip:sip.cybercity.dk", + "sip.uri.scheme": "sip", + "sip.user_agent.original": "Nero SIPPS IP Phone Version 2.0.51.16", + "sip.version": "2.0", + "sip.via.original": [ + "SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp112903503-43a64480192.168.1.2;rport" + ], + "source.ip": "192.168.1.2", + "source.port": 5060, + "status": "OK", + "type": "sip", + "user.name": "voi18062" +} diff --git a/packetbeat/docs/fields.asciidoc b/packetbeat/docs/fields.asciidoc index 2c73f3dd277..14ed56f1578 100644 --- a/packetbeat/docs/fields.asciidoc +++ b/packetbeat/docs/fields.asciidoc @@ -35,6 +35,7 @@ grouped in the following categories: * <> * <> * <> +* <> * <> * <> * <> @@ -11680,6 +11681,667 @@ The return value of the Redis command in a human readable format. If the Redis command has resulted in an error, this field contains the error message returned by the Redis server. +-- + +[[exported-fields-sip]] +== SIP fields + +SIP-specific event fields. + + +[float] +=== sip + +Information about SIP traffic. + + +*`sip.timestamp`*:: ++ +-- +Timestamp with nano second precision. + +type: date_nanos + +-- + +*`sip.code`*:: ++ +-- +Response status code. + +type: keyword + +-- + +*`sip.method`*:: ++ +-- +Request method. + +type: keyword + +-- + +*`sip.status`*:: ++ +-- +Response status phrase. + +type: keyword + +-- + +*`sip.type`*:: ++ +-- +Either request or response. + +type: keyword + +-- + +*`sip.version`*:: ++ +-- +SIP protocol version. + +type: keyword + +-- + +*`sip.uri.original`*:: ++ +-- +The original URI. + +type: keyword + +-- + +*`sip.uri.original.text`*:: ++ +-- +type: text + +-- + +*`sip.uri.scheme`*:: ++ +-- +The URI scheme. + +type: keyword + +-- + +*`sip.uri.username`*:: ++ +-- +The URI user name. + +type: keyword + +-- + +*`sip.uri.host`*:: ++ +-- +The URI host. + +type: keyword + +-- + +*`sip.uri.port`*:: ++ +-- +The URI port. + +type: keyword + +-- + +*`sip.accept`*:: ++ +-- +Accept header value. + +type: keyword + +-- + +*`sip.allow`*:: ++ +-- +Allowed methods. + +type: keyword + +-- + +*`sip.call_id`*:: ++ +-- +Call ID. + +type: keyword + +-- + +*`sip.content_length`*:: ++ +-- +type: long + +-- + +*`sip.content_type`*:: ++ +-- +type: keyword + +-- + +*`sip.max_forwards`*:: ++ +-- +type: long + +-- + +*`sip.supported`*:: ++ +-- +Supported methods. + +type: keyword + +-- + +*`sip.user_agent.original`*:: ++ +-- +type: keyword + +-- + +*`sip.user_agent.original.text`*:: ++ +-- +type: text + +-- + +*`sip.private.uri.original`*:: ++ +-- +Private original URI. + +type: keyword + +-- + +*`sip.private.uri.original.text`*:: ++ +-- +type: text + +-- + +*`sip.private.uri.scheme`*:: ++ +-- +Private URI scheme. + +type: keyword + +-- + +*`sip.private.uri.username`*:: ++ +-- +Private URI user name. + +type: keyword + +-- + +*`sip.private.uri.host`*:: ++ +-- +Private URI host. + +type: keyword + +-- + +*`sip.private.uri.port`*:: ++ +-- +Private URI port. + +type: keyword + +-- + +*`sip.cseq.code`*:: ++ +-- +Sequence code. + +type: keyword + +-- + +*`sip.cseq.method`*:: ++ +-- +Sequence method. + +type: keyword + +-- + +*`sip.via.original`*:: ++ +-- +The original Via value. + +type: keyword + +-- + +*`sip.via.original.text`*:: ++ +-- +type: text + +-- + +*`sip.to.display_info`*:: ++ +-- +To display info + +type: keyword + +-- + +*`sip.to.uri.original`*:: ++ +-- +To original URI + +type: keyword + +-- + +*`sip.to.uri.original.text`*:: ++ +-- +type: text + +-- + +*`sip.to.uri.scheme`*:: ++ +-- +To URI scheme + +type: keyword + +-- + +*`sip.to.uri.username`*:: ++ +-- +To URI user name + +type: keyword + +-- + +*`sip.to.uri.host`*:: ++ +-- +To URI host + +type: keyword + +-- + +*`sip.to.uri.port`*:: ++ +-- +To URI port + +type: keyword + +-- + +*`sip.to.tag`*:: ++ +-- +To tag + +type: keyword + +-- + +*`sip.from.display_info`*:: ++ +-- +From display info + +type: keyword + +-- + +*`sip.from.uri.original`*:: ++ +-- +From original URI + +type: keyword + +-- + +*`sip.from.uri.original.text`*:: ++ +-- +type: text + +-- + +*`sip.from.uri.scheme`*:: ++ +-- +From URI scheme + +type: keyword + +-- + +*`sip.from.uri.username`*:: ++ +-- +From URI user name + +type: keyword + +-- + +*`sip.from.uri.host`*:: ++ +-- +From URI host + +type: keyword + +-- + +*`sip.from.uri.port`*:: ++ +-- +From URI port + +type: keyword + +-- + +*`sip.from.tag`*:: ++ +-- +From tag + +type: keyword + +-- + +*`sip.contact.display_info`*:: ++ +-- +Contact display info + +type: keyword + +-- + +*`sip.contact.uri.original`*:: ++ +-- +Contact original URI + +type: keyword + +-- + +*`sip.contact.uri.original.text`*:: ++ +-- +type: text + +-- + +*`sip.contact.uri.scheme`*:: ++ +-- +Contat URI scheme + +type: keyword + +-- + +*`sip.contact.uri.username`*:: ++ +-- +Contact URI user name + +type: keyword + +-- + +*`sip.contact.uri.host`*:: ++ +-- +Contact URI host + +type: keyword + +-- + +*`sip.contact.uri.port`*:: ++ +-- +Contact URI port + +type: keyword + +-- + +*`sip.contact.transport`*:: ++ +-- +Contact transport + +type: keyword + +-- + +*`sip.contact.line`*:: ++ +-- +Contact line + +type: keyword + +-- + +*`sip.contact.expires`*:: ++ +-- +Contact expires + +type: keyword + +-- + +*`sip.contact.q`*:: ++ +-- +Contact Q + +type: keyword + +-- + +*`sip.auth.scheme`*:: ++ +-- +Auth scheme + +type: keyword + +-- + +*`sip.auth.realm`*:: ++ +-- +Auth realm + +type: keyword + +-- + +*`sip.auth.uri.original`*:: ++ +-- +Auth original URI + +type: keyword + +-- + +*`sip.auth.uri.original.text`*:: ++ +-- +type: text + +-- + +*`sip.auth.uri.scheme`*:: ++ +-- +Auth URI scheme + +type: keyword + +-- + +*`sip.auth.uri.host`*:: ++ +-- +Auth URI host + +type: keyword + +-- + +*`sip.auth.uri.port`*:: ++ +-- +Auth URI port + +type: keyword + +-- + +*`sip.sdp.version`*:: ++ +-- +SDP version + +type: keyword + +-- + +*`sip.sdp.owner.username`*:: ++ +-- +SDP owner user name + +type: keyword + +-- + +*`sip.sdp.owner.session_id`*:: ++ +-- +SDP owner session ID + +type: keyword + +-- + +*`sip.sdp.owner.version`*:: ++ +-- +SDP owner version + +type: keyword + +-- + +*`sip.sdp.owner.ip`*:: ++ +-- +SDP owner IP + +type: ip + +-- + +*`sip.sdp.session.name`*:: ++ +-- +SDP session name + +type: keyword + +-- + +*`sip.sdp.connection.info`*:: ++ +-- +SDP connection info + +type: keyword + +-- + +*`sip.sdp.connection.address`*:: ++ +-- +SDP connection address + +type: keyword + +-- + +*`sip.sdp.body.original`*:: ++ +-- +SDP original body + +type: keyword + +-- + +*`sip.sdp.body.original.text`*:: ++ +-- +type: text + -- [[exported-fields-thrift]] diff --git a/packetbeat/docs/shared-protocol-list.asciidoc b/packetbeat/docs/shared-protocol-list.asciidoc index 3e18fc35eb8..fdac127050a 100644 --- a/packetbeat/docs/shared-protocol-list.asciidoc +++ b/packetbeat/docs/shared-protocol-list.asciidoc @@ -18,3 +18,4 @@ - Memcache - NFS - TLS + - SIP/SDP (beta) diff --git a/packetbeat/include/list.go b/packetbeat/include/list.go index 0dc1f0bd053..748d525eb2f 100644 --- a/packetbeat/include/list.go +++ b/packetbeat/include/list.go @@ -33,6 +33,7 @@ import ( _ "github.com/elastic/beats/v7/packetbeat/protos/nfs" _ "github.com/elastic/beats/v7/packetbeat/protos/pgsql" _ "github.com/elastic/beats/v7/packetbeat/protos/redis" + _ "github.com/elastic/beats/v7/packetbeat/protos/sip" _ "github.com/elastic/beats/v7/packetbeat/protos/thrift" _ "github.com/elastic/beats/v7/packetbeat/protos/tls" ) diff --git a/packetbeat/packetbeat.docker.yml b/packetbeat/packetbeat.docker.yml index edffce72694..4cf9016a926 100644 --- a/packetbeat/packetbeat.docker.yml +++ b/packetbeat/packetbeat.docker.yml @@ -37,6 +37,9 @@ packetbeat.protocols.cassandra: packetbeat.protocols.tls: ports: [443, 993, 995, 5223, 8443, 8883, 9243] +packetbeat.protocols.sip: + ports: [5060] + processors: - add_cloud_metadata: ~ - add_docker_metadata: ~ diff --git a/packetbeat/packetbeat.reference.yml b/packetbeat/packetbeat.reference.yml index 0dc551698e9..6b936240bbb 100644 --- a/packetbeat/packetbeat.reference.yml +++ b/packetbeat/packetbeat.reference.yml @@ -531,6 +531,19 @@ packetbeat.protocols: # Set to true to publish fields with null values in events. #keep_null: false +- type: sip + # Configure the ports where to listen for SIP traffic. You can disable the SIP protocol by commenting out the list of ports. + ports: [5060] + + # Parse the authorization headers + parse_authorization: true + + # Parse body contents (only when body is SDP) + parse_body: true + + # Preserve original contents in event.original + keep_original: true + # ============================ Monitored processes ============================= # Packetbeat can enrich events with information about the process associated diff --git a/packetbeat/packetbeat.yml b/packetbeat/packetbeat.yml index 53f87d73003..31c229b1ef7 100644 --- a/packetbeat/packetbeat.yml +++ b/packetbeat/packetbeat.yml @@ -101,6 +101,10 @@ packetbeat.protocols: - 8883 # Secure MQTT - 9243 # Elasticsearch +- type: sip + # Configure the ports where to listen for SIP traffic. You can disable the SIP protocol by commenting out the list of ports. + ports: [5060] + # ======================= Elasticsearch template setting ======================= setup.template.settings: diff --git a/packetbeat/pb/ecs.go b/packetbeat/pb/ecs.go index b7722c2c22a..4594f7cd8c4 100644 --- a/packetbeat/pb/ecs.go +++ b/packetbeat/pb/ecs.go @@ -54,7 +54,11 @@ type ecsRelated struct { User []string `ecs:"user"` // overridden because this needs to be an array Hash []string `ecs:"hash"` + // overridden because this needs to be an array + Hosts []string `ecs:"hosts"` // for de-dup - ipSet map[string]struct{} + ipSet map[string]struct{} + userSet map[string]struct{} + hostSet map[string]struct{} } diff --git a/packetbeat/pb/event.go b/packetbeat/pb/event.go index f0287665c0d..cd652756f3e 100644 --- a/packetbeat/pb/event.go +++ b/packetbeat/pb/event.go @@ -147,10 +147,15 @@ func (f *Fields) SetDestination(endpoint *common.Endpoint) { func (f *Fields) AddIP(ip ...string) { if f.Related == nil { f.Related = &ecsRelated{ - ipSet: make(map[string]struct{}), + ipSet: make(map[string]struct{}), + userSet: make(map[string]struct{}), + hostSet: make(map[string]struct{}), } } for _, ipAddress := range ip { + if ipAddress == "" { + continue + } if _, ok := f.Related.ipSet[ipAddress]; !ok { f.Related.ipSet[ipAddress] = struct{}{} f.Related.IP = append(f.Related.IP, ipAddress) @@ -158,6 +163,46 @@ func (f *Fields) AddIP(ip ...string) { } } +// AddUser adds the given user names to the related ECS User field +func (f *Fields) AddUser(u ...string) { + if f.Related == nil { + f.Related = &ecsRelated{ + ipSet: make(map[string]struct{}), + userSet: make(map[string]struct{}), + hostSet: make(map[string]struct{}), + } + } + for _, user := range u { + if user == "" { + continue + } + if _, ok := f.Related.userSet[user]; !ok { + f.Related.userSet[user] = struct{}{} + f.Related.User = append(f.Related.User, user) + } + } +} + +// AddHost adds the given hosts to the related ECS Hosts field +func (f *Fields) AddHost(h ...string) { + if f.Related == nil { + f.Related = &ecsRelated{ + ipSet: make(map[string]struct{}), + userSet: make(map[string]struct{}), + hostSet: make(map[string]struct{}), + } + } + for _, host := range h { + if host == "" { + continue + } + if _, ok := f.Related.hostSet[host]; !ok { + f.Related.hostSet[host] = struct{}{} + f.Related.Hosts = append(f.Related.Hosts, host) + } + } +} + func makeProcess(p *common.Process) *ecs.Process { return &ecs.Process{ Name: p.Name, diff --git a/packetbeat/protos/sip/README.md b/packetbeat/protos/sip/README.md new file mode 100644 index 00000000000..5e5962675a1 --- /dev/null +++ b/packetbeat/protos/sip/README.md @@ -0,0 +1,123 @@ +# SIP (Session Initiation Protocol) for packetbeat + +The SIP (Session Initiation Protocol) is a communications protocol for signaling and controlling multimedia communication sessions. SIP is used by many VoIP applications, not only for enterprise uses but also telecom carriers. + +SIP is a text-based protocol like HTTP. But SIP has various unique features like : +- SIP is server-client model, but its role may change in a per call basis. +- SIP is request-response model, but server may (usually) reply with many responses to a single request. +- There are many requests and responses in one call. +- It is not known when the call will end. + +## Implementation + +### Published for each SIP message (request or response) + +- SIP is not a one to one message with request and response. Also order to each message is not determined (a response may be sent after previous response). +- Therefore the SIP responses and requests are published when packetbeat receives them immediately. +- If you need all SIP messages in throughout of SIP dialog, you need to retrieve from Elasticsearch using the SIP Call ID field etc. + +### Notes +* ``transport=tcp`` is not supported yet. +* ``content-encoding`` is not supported yet. +* Default timestamp field(@timestamp) precision is not sufficient(the sip response is often send immediately when request received eg. 100 Trying). You can sort to keep the message correct order using the ``sip.timestamp``(`date_nanos`) field. +* Body parsing is partially supported for ``application/sdp`` content type only. + +## Configuration + +```yaml +- type: sip + # Configure the ports where to listen for SIP traffic. You can disable the SIP protocol by commenting out the list of ports. + ports: [5060] + + # Parse the authorization headers + parse_authorization: true + + # Parse body contents (only when body is SDP) + parse_body: true + + # Preserve original contents in event.original + keep_original: true +``` + +### Sample Full JSON Output + +```json +{ + "@metadata.beat": "packetbeat", + "@metadata.type": "_doc", + "client.ip": "192.168.1.2", + "client.port": 5060, + "destination.ip": "212.242.33.35", + "destination.port": 5060, + "event.action": "sip_register", + "event.category": [ + "network", + "protocol", + "authentication" + ], + "event.dataset": "sip", + "event.duration": 0, + "event.kind": "event", + "event.original": "REGISTER sip:sip.cybercity.dk SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp112903503-43a64480192.168.1.2;rport\r\nFrom: ;tag=6bac55c\r\nTo: \r\nCall-ID: 578222729-4665d775@578222732-4665d772\r\nContact: ;expires=1200;q=0.500\r\nExpires: 1200\r\nCSeq: 75 REGISTER\r\nContent-Length: 0\r\nAuthorization: Digest username=\"voi18062\",realm=\"sip.cybercity.dk\",uri=\"sip:192.168.1.2\",nonce=\"1701b22972b90f440c3e4eb250842bb\",opaque=\"1701a1351f70795\",nc=\"00000001\",response=\"79a0543188495d288c9ebbe0c881abdc\"\r\nMax-Forwards: 70\r\nUser-Agent: Nero SIPPS IP Phone Version 2.0.51.16\r\n\r\n", + "event.sequence": 75, + "event.type": [ + "info" + ], + "network.application": "sip", + "network.community_id": "1:dOa61R2NaaJsJlcFAiMIiyXX+Kk=", + "network.iana_number": "17", + "network.protocol": "sip", + "network.transport": "udp", + "network.type": "ipv4", + "related.hosts": [ + "sip.cybercity.dk" + ], + "related.ip": [ + "192.168.1.2", + "212.242.33.35" + ], + "related.user": [ + "voi18062" + ], + "server.ip": "212.242.33.35", + "server.port": 5060, + "sip.auth.realm": "sip.cybercity.dk", + "sip.auth.scheme": "Digest", + "sip.auth.uri.host": "192.168.1.2", + "sip.auth.uri.original": "sip:192.168.1.2", + "sip.auth.uri.scheme": "sip", + "sip.call_id": "578222729-4665d775@578222732-4665d772", + "sip.contact.uri.host": "sip.cybercity.dk", + "sip.contact.uri.original": "sip:voi18062@sip.cybercity.dk", + "sip.contact.uri.scheme": "sip", + "sip.contact.uri.username": "voi18062", + "sip.cseq.code": 75, + "sip.cseq.method": "REGISTER", + "sip.from.tag": "6bac55c", + "sip.from.uri.host": "sip.cybercity.dk", + "sip.from.uri.original": "sip:voi18062@sip.cybercity.dk", + "sip.from.uri.scheme": "sip", + "sip.from.uri.username": "voi18062", + "sip.max_forwards": 70, + "sip.method": "REGISTER", + "sip.to.uri.host": "sip.cybercity.dk", + "sip.to.uri.original": "sip:voi18062@sip.cybercity.dk", + "sip.to.uri.scheme": "sip", + "sip.to.uri.username": "voi18062", + "sip.type": "request", + "sip.uri.host": "sip.cybercity.dk", + "sip.uri.original": "sip:sip.cybercity.dk", + "sip.uri.scheme": "sip", + "sip.user_agent.original": "Nero SIPPS IP Phone Version 2.0.51.16", + "sip.version": "2.0", + "sip.via.original": [ + "SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp112903503-43a64480192.168.1.2;rport" + ], + "source.ip": "192.168.1.2", + "source.port": 5060, + "status": "OK", + "type": "sip", + "user.name": "voi18062" +} +``` + diff --git a/packetbeat/protos/sip/_meta/fields.yml b/packetbeat/protos/sip/_meta/fields.yml new file mode 100644 index 00000000000..fcbb1349dd1 --- /dev/null +++ b/packetbeat/protos/sip/_meta/fields.yml @@ -0,0 +1,238 @@ +- key: sip + title: "SIP" + description: SIP-specific event fields. + fields: + - name: sip + type: group + description: Information about SIP traffic. + fields: + - name: timestamp + type: date_nanos + description: Timestamp with nano second precision. + - name: code + type: keyword + description: Response status code. + - name: method + type: keyword + description: Request method. + - name: status + type: keyword + description: Response status phrase. + - name: type + type: keyword + description: Either request or response. + - name: version + type: keyword + description: SIP protocol version. + - name: uri.original + type: keyword + description: The original URI. + multi_fields: + - name: text + type: text + analyzer: simple + - name: uri.scheme + type: keyword + description: The URI scheme. + - name: uri.username + type: keyword + description: The URI user name. + - name: uri.host + type: keyword + description: The URI host. + - name: uri.port + type: keyword + description: The URI port. + - name: accept + type: keyword + description: Accept header value. + - name: allow + type: keyword + description: Allowed methods. + - name: call_id + type: keyword + description: Call ID. + - name: content_length + type: long + - name: content_type + type: keyword + - name: max_forwards + type: long + - name: supported + type: keyword + description: Supported methods. + - name: user_agent.original + type: keyword + multi_fields: + - name: text + type: text + analyzer: simple + - name: private.uri.original + type: keyword + description: Private original URI. + multi_fields: + - name: text + type: text + analyzer: simple + - name: private.uri.scheme + type: keyword + description: Private URI scheme. + - name: private.uri.username + type: keyword + description: Private URI user name. + - name: private.uri.host + type: keyword + description: Private URI host. + - name: private.uri.port + type: keyword + description: Private URI port. + - name: cseq.code + type: keyword + description: Sequence code. + - name: cseq.method + type: keyword + description: Sequence method. + - name: via.original + type: keyword + description: The original Via value. + multi_fields: + - name: text + type: text + analyzer: simple + - name: to.display_info + type: keyword + description: "To display info" + - name: to.uri.original + type: keyword + description: "To original URI" + multi_fields: + - name: text + type: text + analyzer: simple + - name: to.uri.scheme + type: keyword + description: "To URI scheme" + - name: to.uri.username + type: keyword + description: "To URI user name" + - name: to.uri.host + type: keyword + description: "To URI host" + - name: to.uri.port + type: keyword + description: "To URI port" + - name: to.tag + type: keyword + description: "To tag" + - name: from.display_info + type: keyword + description: "From display info" + - name: from.uri.original + type: keyword + description: "From original URI" + multi_fields: + - name: text + type: text + analyzer: simple + - name: from.uri.scheme + type: keyword + description: "From URI scheme" + - name: from.uri.username + type: keyword + description: "From URI user name" + - name: from.uri.host + type: keyword + description: "From URI host" + - name: from.uri.port + type: keyword + description: "From URI port" + - name: from.tag + type: keyword + description: "From tag" + - name: contact.display_info + type: keyword + description: "Contact display info" + - name: contact.uri.original + type: keyword + description: "Contact original URI" + multi_fields: + - name: text + type: text + analyzer: simple + - name: contact.uri.scheme + type: keyword + description: "Contat URI scheme" + - name: contact.uri.username + type: keyword + description: "Contact URI user name" + - name: contact.uri.host + type: keyword + description: "Contact URI host" + - name: contact.uri.port + type: keyword + description: "Contact URI port" + - name: contact.transport + type: keyword + description: "Contact transport" + - name: contact.line + type: keyword + description: "Contact line" + - name: contact.expires + type: keyword + description: "Contact expires" + - name: contact.q + type: keyword + description: "Contact Q" + - name: auth.scheme + type: keyword + description: "Auth scheme" + - name: auth.realm + type: keyword + description: "Auth realm" + - name: auth.uri.original + type: keyword + description: "Auth original URI" + multi_fields: + - name: text + type: text + analyzer: simple + - name: auth.uri.scheme + type: keyword + description: "Auth URI scheme" + - name: auth.uri.host + type: keyword + description: "Auth URI host" + - name: auth.uri.port + type: keyword + description: "Auth URI port" + - name: sdp.version + type: keyword + description: "SDP version" + - name: sdp.owner.username + type: keyword + description: "SDP owner user name" + - name: sdp.owner.session_id + type: keyword + description: "SDP owner session ID" + - name: sdp.owner.version + type: keyword + description: "SDP owner version" + - name: sdp.owner.ip + type: ip + description: "SDP owner IP" + - name: sdp.session.name + type: keyword + description: "SDP session name" + - name: sdp.connection.info + type: keyword + description: "SDP connection info" + - name: sdp.connection.address + type: keyword + description: "SDP connection address" + - name: sdp.body.original + type: keyword + description: "SDP original body" + multi_fields: + - name: text + type: text + analyzer: simple diff --git a/packetbeat/protos/sip/config.go b/packetbeat/protos/sip/config.go new file mode 100644 index 00000000000..58a92606e80 --- /dev/null +++ b/packetbeat/protos/sip/config.go @@ -0,0 +1,41 @@ +// 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 sip + +import ( + cfg "github.com/elastic/beats/v7/packetbeat/config" + "github.com/elastic/beats/v7/packetbeat/protos" +) + +type config struct { + cfg.ProtocolCommon `config:",inline"` + ParseAuthorization bool `config:"parse_authorization"` + ParseBody bool `config:"parse_body"` + KeepOriginal bool `config:"keep_original"` +} + +var ( + defaultConfig = config{ + ProtocolCommon: cfg.ProtocolCommon{ + TransactionTimeout: protos.DefaultTransactionExpiration, + }, + ParseAuthorization: true, + ParseBody: true, + KeepOriginal: true, + } +) diff --git a/packetbeat/protos/sip/event.go b/packetbeat/protos/sip/event.go new file mode 100644 index 00000000000..fcb9112fe93 --- /dev/null +++ b/packetbeat/protos/sip/event.go @@ -0,0 +1,102 @@ +// 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 sip + +import ( + "github.com/elastic/beats/v7/libbeat/common" +) + +// ProtocolFields contains SIP fields. +type ProtocolFields struct { + Timestamp int64 `ecs:"timestamp"` + Code int `ecs:"code"` + Method common.NetString `ecs:"method"` + Status common.NetString `ecs:"status"` + Type string `ecs:"type"` + Version string `ecs:"version"` + + URIOriginal common.NetString `ecs:"uri.original"` + URIScheme common.NetString `ecs:"uri.scheme"` + URIUsername common.NetString `ecs:"uri.username"` + URIHost common.NetString `ecs:"uri.host"` + URIPort int `ecs:"uri.port"` + + Accept common.NetString `ecs:"accept"` + Allow []string `ecs:"allow"` + CallID common.NetString `ecs:"call_id"` + ContentLength int `ecs:"content_length"` + ContentType common.NetString `ecs:"content_type"` + MaxForwards int `ecs:"max_forwards"` + Supported []string `ecs:"supported"` + UserAgentOriginal common.NetString `ecs:"user_agent.original"` + + PrivateURIOriginal common.NetString `ecs:"private.uri.original"` + PrivateURIScheme common.NetString `ecs:"private.uri.scheme"` + PrivateURIUsername common.NetString `ecs:"private.uri.username"` + PrivateURIHost common.NetString `ecs:"private.uri.host"` + PrivateURIPort int `ecs:"private.uri.port"` + + CseqCode int `ecs:"cseq.code"` + CseqMethod common.NetString `ecs:"cseq.method"` + + ViaOriginal []common.NetString `ecs:"via.original"` + + ToDisplayInfo common.NetString `ecs:"to.display_info"` + ToURIOriginal common.NetString `ecs:"to.uri.original"` + ToURIScheme common.NetString `ecs:"to.uri.scheme"` + ToURIUsername common.NetString `ecs:"to.uri.username"` + ToURIHost common.NetString `ecs:"to.uri.host"` + ToURIPort int `ecs:"to.uri.port"` + ToTag common.NetString `ecs:"to.tag"` + + FromDisplayInfo common.NetString `ecs:"from.display_info"` + FromURIOriginal common.NetString `ecs:"from.uri.original"` + FromURIScheme common.NetString `ecs:"from.uri.scheme"` + FromURIUsername common.NetString `ecs:"from.uri.username"` + FromURIHost common.NetString `ecs:"from.uri.host"` + FromURIPort int `ecs:"from.uri.port"` + FromTag common.NetString `ecs:"from.tag"` + + ContactDisplayInfo common.NetString `ecs:"contact.display_info"` + ContactURIOriginal common.NetString `ecs:"contact.uri.original"` + ContactURIScheme common.NetString `ecs:"contact.uri.scheme"` + ContactURIUsername common.NetString `ecs:"contact.uri.username"` + ContactURIHost common.NetString `ecs:"contact.uri.host"` + ContactURIPort int `ecs:"contact.uri.port"` + ContactTransport common.NetString `ecs:"contact.transport"` + ContactLine common.NetString `ecs:"contact.line"` + ContactExpires int `ecs:"contact.expires"` + ContactQ float64 `ecs:"contact.q"` + + AuthScheme common.NetString `ecs:"auth.scheme"` + AuthRealm common.NetString `ecs:"auth.realm"` + AuthURIOriginal common.NetString `ecs:"auth.uri.original"` + AuthURIScheme common.NetString `ecs:"auth.uri.scheme"` + AuthURIHost common.NetString `ecs:"auth.uri.host"` + AuthURIPort int `ecs:"auth.uri.port"` + + SDPVersion string `ecs:"sdp.version"` + SDPOwnerUsername common.NetString `ecs:"sdp.owner.username"` + SDPOwnerSessID common.NetString `ecs:"sdp.owner.session_id"` + SDPOwnerVersion common.NetString `ecs:"sdp.owner.version"` + SDPOwnerIP common.NetString `ecs:"sdp.owner.ip"` + SDPSessName common.NetString `ecs:"sdp.session.name"` + SDPConnInfo common.NetString `ecs:"sdp.connection.info"` + SDPConnAddr common.NetString `ecs:"sdp.connection.address"` + SDPBodyOriginal common.NetString `ecs:"sdp.body.original"` +} diff --git a/packetbeat/protos/sip/fields.go b/packetbeat/protos/sip/fields.go new file mode 100644 index 00000000000..87c93ab0cad --- /dev/null +++ b/packetbeat/protos/sip/fields.go @@ -0,0 +1,36 @@ +// 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. + +// Code generated by beats/dev-tools/cmd/asset/asset.go - DO NOT EDIT. + +package sip + +import ( + "github.com/elastic/beats/v7/libbeat/asset" +) + +func init() { + if err := asset.SetFields("packetbeat", "sip", asset.ModuleFieldsPri, AssetSip); err != nil { + panic(err) + } +} + +// AssetSip returns asset data. +// This is the base64 encoded gzipped contents of protos/sip. +func AssetSip() string { + return "eJzEmcFy4jgQhu95ii7u8QNwm5rsVnHLhsxeKY3dxqqRJUdqQ9in35KxFGEkZqRUhZzAcX+/1Pz9ozKP8AtPazB8eAAgTgLXsNpunlcPAA2aWvOBuJJr2G6eH82ANW95DXhASdByFI2pHmB+tX4AAHgEyXp0SPtHpwHXsNdqdFcuyBvZKt0z+wbYTzWS1QLSrG15Xc0VocKHBvEeDbHecZ1Wwwh3kkll/D8uJF9dHRw5dWDvBIO1kg0MGmtuuJLVQqtWDS5kfuHpqHQT13hBMyhpEAwxGs1Uv2T2SJ1q8qhvIxqaK5e8s9RnVjl0mpmrdVpQDvUvTh1q0PNilX151lmSD6hts3Pg1h2DVqRqJVz9EjtqXinN91wykcN+7RBcHfx42VT+tn4UxHeXNrxoEb5TcNmpXV1mkonTf6jtgPSDwMjCTd1hn9Vvu+wfLxs4V8aaMRrU9l0J1dZOqBi4U4ZKoLYuxhuULuLZuiWP1TUOWbRvUwV0yBrUcGBivNo0E0Ids5i2AJt5ZM1VrjAhdjwrBL4zIWDzdJ1QklDSTqDcU7cACiX3ift/O90+rtj7rlX6yHSzDJkI3oyD/VAwa2tbV5RqlzXjju1R0p8O+BdN7qD5gRFWpdHzfK6/b/yEm8iPIbeFdBSF/JJIChWSsRSK5MZTKBCLqJCdG1UhOxZXtcG3KveYsbXfsbLG6PliIuYfMjwzfso4cPb5L9d/ObvM1y/zOKmq4WYQ7LTjslU5O1i9KphrwdaurtGl42/R4eiv7tCWsqm3K/+Y+ERLSobdgf2gJ9i5M+64ti6BzB1th7R1ESSxfS6N2H4JarXqy637t1b9TfNO+GL7Tvi7Gtivv8DC0+rTJvboIht7eNLInp9tZc+Omdljs+3ssTFDT9hcS0/EiKntIZTVVO7r72fATWs7kWJ3O5G7GjzcRYHHpz3QDZeH/CKjuy7d9Hqokm33UCHm+BCebfoQHvO9g5Nm0hTTfXUKL7gsa7stTEHxfeAasx4Nee5cm0K/FUH/WeLYSF2Jrb+N1CUMPSE1MtHnE6eyKLA4RCbuXRPEr7+0z+nw8Ojsmfbg2EB7bPa8eWxs1kwzVAWPHlfbp2f3yDGGVEeJuiw7LXkqTyfnh4JBY5eQ+dgo0JgBsHlKi5T256zw2y7x5UP74EIKev5dYsmbN1MV9dx1ItXwWkmJtS2oso8mlv9RHz2ZLCRY02g0eTG9UJkRMaGfqjmVRdf0EbjkspgviK7/AwAA///sesAq" +} diff --git a/packetbeat/protos/sip/parser.go b/packetbeat/protos/sip/parser.go new file mode 100644 index 00000000000..55e66045e95 --- /dev/null +++ b/packetbeat/protos/sip/parser.go @@ -0,0 +1,469 @@ +// 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 sip + +import ( + "bytes" + "errors" + "fmt" + "time" + "unicode" + + "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/common/streambuf" + "github.com/elastic/beats/v7/packetbeat/procs" + "github.com/elastic/beats/v7/packetbeat/protos" +) + +// Http Message +type message struct { + ts time.Time + hasContentLength bool + headerOffset int + + isRequest bool + ipPortTuple common.IPPortTuple + cmdlineTuple *common.ProcessTuple + + // Info + requestURI common.NetString + method common.NetString + statusCode uint16 + statusPhrase common.NetString + version version + + // Headers + contentLength int + contentType common.NetString + userAgent common.NetString + to common.NetString + from common.NetString + cseq common.NetString + callID common.NetString + maxForwards int + via []common.NetString + allow []string + supported []string + + headers map[string][]common.NetString + size uint64 + + firstLine []byte + rawHeaders []byte + body []byte + rawData []byte +} + +type version struct { + major uint8 + minor uint8 +} + +func (v version) String() string { + return fmt.Sprintf("%d.%d", v.major, v.minor) +} + +type parserState uint8 + +const ( + stateStart parserState = iota + stateHeaders + stateBody +) + +type parser struct { +} + +type parsingInfo struct { + tuple *common.IPPortTuple + + data []byte + + parseOffset int + state parserState + bodyReceived int + + pkt *protos.Packet +} + +var ( + constCRLF = []byte("\r\n") + constSIPVersion = []byte("SIP/") + nameContentLength = []byte("content-length") + nameContentType = []byte("content-type") + nameUserAgent = []byte("user-agent") + nameTo = []byte("to") + nameFrom = []byte("from") + nameCseq = []byte("cseq") + nameCallID = []byte("call-id") + nameMaxForwards = []byte("max-forwards") + nameAllow = []byte("allow") + nameSupported = []byte("supported") + nameVia = []byte("via") +) + +func newParser() *parser { + return &parser{} +} + +func (parser *parser) parse(pi *parsingInfo) (*message, error) { + m := &message{ + ts: pi.pkt.Ts, + ipPortTuple: pi.pkt.Tuple, + cmdlineTuple: procs.ProcWatcher.FindProcessesTupleTCP(&pi.pkt.Tuple), + rawData: pi.data, + } + for pi.parseOffset < len(pi.data) { + switch pi.state { + case stateStart: + if err := parser.parseSIPLine(pi, m); err != nil { + return m, err + } + case stateHeaders: + if err := parser.parseHeaders(pi, m); err != nil { + return m, err + } + case stateBody: + parser.parseBody(pi, m) + } + } + return m, nil +} + +func (*parser) parseSIPLine(pi *parsingInfo, m *message) error { + // ignore any CRLF appearing before the start-line (RFC3261 7.5) + pi.data = bytes.TrimLeft(pi.data[pi.parseOffset:], string(constCRLF)) + + i := bytes.Index(pi.data[pi.parseOffset:], constCRLF) + if i == -1 { + return errors.New("not found expected CRLF") + } + + // Very basic tests on the first line. Just to check that + // we have what looks as a SIP message + var ( + version []byte + err error + ) + + fline := pi.data[pi.parseOffset:i] + if len(fline) < 16 { // minimum line will be "SIP/2.0 XXX OK\r\n" + if isDebug { + debugf("First line too small") + } + return errors.New("first line too small") + } + + m.firstLine = fline + + if bytes.Equal(fline[0:4], constSIPVersion) { + // RESPONSE + m.isRequest = false + version = fline[4:7] + m.statusCode, m.statusPhrase, err = parseResponseStatus(fline[8:]) + if err != nil { + if isDebug { + debugf("Failed to understand SIP response status: %s", fline[8:]) + } + return errors.New("failed to parse response status") + } + + if isDebug { + debugf("SIP status_code=%d, status_phrase=%s", m.statusCode, m.statusPhrase) + } + } else { + // REQUEST + afterMethodIdx := bytes.IndexFunc(fline, unicode.IsSpace) + afterRequestURIIdx := bytes.LastIndexFunc(fline, unicode.IsSpace) + + // Make sure we have the VERB + URI + SIP_VERSION + if afterMethodIdx == -1 || afterRequestURIIdx == -1 || afterMethodIdx == afterRequestURIIdx { + if isDebug { + debugf("Couldn't understand SIP request: %s", fline) + } + return errors.New("failed to parse SIP request") + } + + m.method = common.NetString(fline[:afterMethodIdx]) + m.requestURI = common.NetString(fline[afterMethodIdx+1 : afterRequestURIIdx]) + + versionIdx := afterRequestURIIdx + len(constSIPVersion) + 1 + if len(fline) > versionIdx && bytes.Equal(fline[afterRequestURIIdx+1:versionIdx], constSIPVersion) { + m.isRequest = true + version = fline[versionIdx:] + } else { + if isDebug { + debugf("Couldn't understand SIP version: %s", fline) + } + return errors.New("failed to parse SIP version") + } + } + + m.version.major, m.version.minor, err = parseVersion(version) + if err != nil { + if isDebug { + debugf(err.Error(), version) + } + return err + } + if isDebug { + debugf("SIP version %d.%d", m.version.major, m.version.minor) + } + + // ok so far + pi.parseOffset = i + 2 + m.headerOffset = pi.parseOffset + pi.state = stateHeaders + + return nil +} + +func parseResponseStatus(s []byte) (uint16, []byte, error) { + if isDebug { + debugf("parseResponseStatus: %s", s) + } + + var phrase []byte + p := bytes.IndexByte(s, ' ') + if p == -1 { + p = len(s) + } else { + phrase = s[p+1:] + } + statusCode, err := parseInt(s[0:p]) + if err != nil { + return 0, nil, fmt.Errorf("Unable to parse status code from [%s]", s) + } + return uint16(statusCode), phrase, nil +} + +func parseVersion(s []byte) (uint8, uint8, error) { + if len(s) < 3 { + return 0, 0, errors.New("Invalid version") + } + + major := s[0] - '0' + minor := s[2] - '0' + + return uint8(major), uint8(minor), nil +} + +func (parser *parser) parseHeaders(pi *parsingInfo, m *message) error { + // check if it isn't headers end yet with /r/n/r/n + if !(len(pi.data)-pi.parseOffset >= 2 && + bytes.Equal(pi.data[pi.parseOffset:pi.parseOffset+2], constCRLF)) { + offset, err := parser.parseHeader(m, pi.data[pi.parseOffset:]) + if err != nil { + return err + } + + pi.parseOffset += offset + + return nil + } + + m.size = uint64(pi.parseOffset + 2) + m.rawHeaders = pi.data[:m.size] + pi.data = pi.data[m.size:] + pi.parseOffset = 0 + + if m.contentLength == 0 && (m.isRequest || m.hasContentLength) { + if isDebug { + debugf("Empty content length, ignore body") + } + return nil + } + + if isDebug { + debugf("Read body") + } + + pi.state = stateBody + + return nil +} + +func (parser *parser) parseHeader(m *message, data []byte) (int, error) { + if m.headers == nil { + m.headers = make(map[string][]common.NetString) + } + + i := bytes.Index(data, []byte(":")) + if i == -1 { + // Expected \":\" in headers. Assuming incomplete + if isDebug { + debugf("ignoring incomplete header %s", data) + } + return len(data), nil + } + + // enabled if required. Allocs for parameters slow down parser big times + if isDetailed { + detailedf("Data: %s", data) + detailedf("Header: %s", data[:i]) + } + + // skip folding line + for p := i + 1; p < len(data); { + q := bytes.Index(data[p:], constCRLF) + if q == -1 { + if isDebug { + debugf("ignoring incomplete header %s", data) + } + return len(data), nil + } + + p += q + if len(data) > p && (data[p+1] == ' ' || data[p+1] == '\t') { + p = p + 2 + continue + } + + headerName := getExpandedHeaderName(bytes.ToLower(data[:i])) + headerVal := bytes.TrimSpace(data[i+1 : p]) + if isDebug { + debugf("Header: '%s' Value: '%s'\n", data[:i], headerVal) + } + + // Headers we need for parsing. Make sure we always + // capture their value + switch { + case bytes.Equal(headerName, nameMaxForwards): + m.maxForwards, _ = parseInt(headerVal) + case bytes.Equal(headerName, nameContentLength): + m.contentLength, _ = parseInt(headerVal) + m.hasContentLength = true + case bytes.Equal(headerName, nameContentType): + m.contentType = headerVal + case bytes.Equal(headerName, nameUserAgent): + m.userAgent = headerVal + case bytes.Equal(headerName, nameTo): + m.to = headerVal + case bytes.Equal(headerName, nameFrom): + m.from = headerVal + case bytes.Equal(headerName, nameCseq): + m.cseq = headerVal + case bytes.Equal(headerName, nameCallID): + m.callID = headerVal + case bytes.Equal(headerName, nameAllow): + m.allow = parseCommaSeparatedList(headerVal) + case bytes.Equal(headerName, nameSupported): + m.supported = parseCommaSeparatedList(headerVal) + case bytes.Equal(headerName, nameVia): + m.via = append(m.via, headerVal) + } + + m.headers[string(headerName)] = append( + m.headers[string(headerName)], + headerVal, + ) + + return p + 2, nil + } + + return len(data), nil +} + +func parseCommaSeparatedList(s common.NetString) (list []string) { + values := bytes.Split(s, []byte(",")) + list = make([]string, len(values)) + for idx := range values { + list[idx] = string(bytes.ToLower(bytes.Trim(values[idx], " "))) + } + return list +} + +func (*parser) parseBody(pi *parsingInfo, m *message) { + nbytes := len(pi.data) + if nbytes >= m.contentLength-pi.bodyReceived { + wanted := m.contentLength - pi.bodyReceived + m.body = append(m.body, pi.data[:wanted]...) + pi.bodyReceived = m.contentLength + m.size += uint64(wanted) + pi.data = pi.data[wanted:] + } else { + m.body = append(m.body, pi.data...) + pi.data = nil + pi.bodyReceived += nbytes + m.size += uint64(nbytes) + if isDebug { + debugf("bodyReceived: %d", pi.bodyReceived) + } + } +} + +func (m *message) getEndpoints() (src *common.Endpoint, dst *common.Endpoint) { + source, destination := common.MakeEndpointPair(m.ipPortTuple.BaseTuple, m.cmdlineTuple) + src, dst = &source, &destination + return src, dst +} + +func parseInt(line []byte) (int, error) { + buf := streambuf.NewFixed(line) + i, err := buf.IntASCII(false) + return int(i), err + // TODO: is it an error if 'buf.Len() != 0 {}' ? +} + +func getExpandedHeaderName(n []byte) []byte { + if len(n) > 1 { + return n + } + switch string(n) { + // referfenced by https://www.iana.org/assignments/sip-parameters/sip-parameters.xhtml + case "a": + return []byte("accept-contact") //[RFC3841] + case "b": + return []byte("referred-by") //[RFC3892] + case "c": + return []byte("content-type") //[RFC3261] + case "d": + return []byte("request-disposition") //[RFC3841] + case "e": + return []byte("content-encoding") //[RFC3261] + case "f": + return []byte("from") //[RFC3261] + case "i": + return []byte("call-id") //[RFC3261] + case "j": + return []byte("reject-contact") //[RFC3841] + case "k": + return []byte("supported") //[RFC3261] + case "l": + return []byte("content-length") //[RFC3261] + case "m": + return []byte("contact") //[RFC3261] + case "o": + return []byte("event") //[RFC666)5] [RFC6446] + case "r": + return []byte("refer-to") //[RFC3515] + case "s": + return []byte("subject") //[RFC3261] + case "t": + return []byte("to") //[RFC3261] + case "u": + return []byte("allow-events") //[RFC6665] + case "v": + return []byte("via") //[RFC326)1] [RFC7118] + case "x": + return []byte("session-expires") //[RFC4028] + case "y": + return []byte("identity") //[RFC8224] + } + return n +} diff --git a/packetbeat/protos/sip/plugin.go b/packetbeat/protos/sip/plugin.go new file mode 100644 index 00000000000..e4cc0364d9a --- /dev/null +++ b/packetbeat/protos/sip/plugin.go @@ -0,0 +1,617 @@ +// 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 sip + +import ( + "bytes" + "fmt" + "strconv" + "strings" + "time" + + "github.com/elastic/beats/v7/libbeat/beat" + "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/common/cfgwarn" + "github.com/elastic/beats/v7/libbeat/logp" + "github.com/elastic/beats/v7/packetbeat/pb" + "github.com/elastic/beats/v7/packetbeat/protos" +) + +var ( + debugf = logp.MakeDebug("sip") + detailedf = logp.MakeDebug("sipdetailed") +) + +// SIP application level protocol analyser plugin. +type plugin struct { + // config + ports []int + parseAuthorization bool + parseBody bool + keepOriginal bool + + results protos.Reporter +} + +var ( + isDebug = false + isDetailed = false +) + +func init() { + protos.Register("sip", New) +} + +func New( + testMode bool, + results protos.Reporter, + cfg *common.Config, +) (protos.Plugin, error) { + cfgwarn.Beta("packetbeat SIP protocol is used") + + p := &plugin{} + config := defaultConfig + if !testMode { + if err := cfg.Unpack(&config); err != nil { + return nil, err + } + } + + if err := p.init(results, &config); err != nil { + return nil, err + } + return p, nil +} + +// Init initializes the HTTP protocol analyser. +func (p *plugin) init(results protos.Reporter, config *config) error { + p.setFromConfig(config) + + isDebug = logp.IsDebug("sip") + isDetailed = logp.IsDebug("sipdetailed") + p.results = results + return nil +} + +func (p *plugin) setFromConfig(config *config) { + p.ports = config.Ports + p.keepOriginal = config.KeepOriginal + p.parseAuthorization = config.ParseAuthorization + p.parseBody = config.ParseBody +} + +func (p *plugin) GetPorts() []int { + return p.ports +} + +func (p *plugin) ParseUDP(pkt *protos.Packet) { + defer logp.Recover("SIP ParseUDP exception") + + if err := p.doParse(pkt); err != nil { + debugf("error: %s", err) + } +} + +func (p *plugin) doParse(pkt *protos.Packet) error { + if isDetailed { + detailedf("Payload received: [%s]", pkt.Payload) + } + + parser := newParser() + + pi := newParsingInfo(pkt) + m, err := parser.parse(pi) + if err != nil { + return err + } + + evt, err := p.buildEvent(m, pkt) + if err != nil { + return err + } + + p.publish(*evt) + + return nil +} + +func (p *plugin) publish(evt beat.Event) { + if p.results != nil { + p.results(evt) + } +} + +func newParsingInfo(pkt *protos.Packet) *parsingInfo { + return &parsingInfo{ + tuple: &pkt.Tuple, + data: pkt.Payload, + pkt: pkt, + } +} + +func (p *plugin) buildEvent(m *message, pkt *protos.Packet) (*beat.Event, error) { + status := common.OK_STATUS + if m.statusCode >= 400 { + status = common.ERROR_STATUS + } + + evt, pbf := pb.NewBeatEvent(m.ts) + fields := evt.Fields + fields["type"] = "sip" + fields["status"] = status + + var sipFields ProtocolFields + sipFields.Timestamp = time.Now().UnixNano() + if m.isRequest { + populateRequestFields(m, pbf, &sipFields) + } else { + populateResponseFields(m, &sipFields) + } + + p.populateHeadersFields(m, evt, pbf, &sipFields) + + if p.parseBody { + populateBodyFields(m, pbf, &sipFields) + } + + pbf.Network.IANANumber = "17" + pbf.Network.Application = "sip" + pbf.Network.Protocol = "sip" + pbf.Network.Transport = "udp" + + src, dst := m.getEndpoints() + pbf.SetSource(src) + pbf.SetDestination(dst) + + p.populateEventFields(m, pbf, sipFields) + + if err := pb.MarshalStruct(evt.Fields, "sip", sipFields); err != nil { + return nil, err + } + + return &evt, nil +} + +func populateRequestFields(m *message, pbf *pb.Fields, fields *ProtocolFields) { + fields.Type = "request" + fields.Method = bytes.ToUpper(m.method) + fields.URIOriginal = m.requestURI + scheme, username, host, port, _ := parseURI(fields.URIOriginal) + fields.URIScheme = scheme + fields.URIHost = host + if !bytes.Equal(username, []byte(" ")) && !bytes.Equal(username, []byte("-")) { + fields.URIUsername = username + pbf.AddUser(string(username)) + } + fields.URIPort = port + fields.Version = m.version.String() + pbf.AddHost(string(host)) +} + +func populateResponseFields(m *message, fields *ProtocolFields) { + fields.Type = "response" + fields.Code = int(m.statusCode) + fields.Status = m.statusPhrase + fields.Version = m.version.String() +} + +func (p *plugin) populateHeadersFields(m *message, evt beat.Event, pbf *pb.Fields, fields *ProtocolFields) { + fields.Allow = m.allow + fields.CallID = m.callID + fields.ContentLength = m.contentLength + fields.ContentType = bytes.ToLower(m.contentType) + fields.MaxForwards = m.maxForwards + fields.Supported = m.supported + fields.UserAgentOriginal = m.userAgent + fields.ViaOriginal = m.via + + privateURI, found := m.headers["p-associated-uri"] + if found && len(privateURI) > 0 { + scheme, username, host, port, _ := parseURI(privateURI[0]) + fields.PrivateURIOriginal = privateURI[0] + fields.PrivateURIScheme = scheme + fields.PrivateURIHost = host + if !bytes.Equal(username, []byte(" ")) && !bytes.Equal(username, []byte("-")) { + fields.PrivateURIUsername = username + pbf.AddUser(string(username)) + } + fields.PrivateURIPort = port + pbf.AddHost(string(host)) + } + + if accept, found := m.headers["accept"]; found && len(accept) > 0 { + fields.Accept = bytes.ToLower(accept[0]) + } + + cseqParts := bytes.Split(m.cseq, []byte(" ")) + if len(cseqParts) == 2 { + fields.CseqCode, _ = strconv.Atoi(string(cseqParts[0])) + fields.CseqMethod = bytes.ToUpper(cseqParts[1]) + } + + populateFromFields(m, pbf, fields) + + populateToFields(m, pbf, fields) + + populateContactFields(m, pbf, fields) + + if p.parseAuthorization { + populateAuthFields(m, evt, pbf, fields) + } +} + +func populateFromFields(m *message, pbf *pb.Fields, fields *ProtocolFields) { + if len(m.from) > 0 { + displayInfo, uri, params := parseFromToContact(m.from) + fields.FromDisplayInfo = displayInfo + fields.FromTag = params["tag"] + scheme, username, host, port, _ := parseURI(uri) + fields.FromURIOriginal = uri + fields.FromURIScheme = scheme + fields.FromURIHost = host + if !bytes.Equal(username, []byte(" ")) && !bytes.Equal(username, []byte("-")) { + fields.FromURIUsername = username + pbf.AddUser(string(username)) + } + fields.FromURIPort = port + pbf.AddHost(string(host)) + } +} + +func populateToFields(m *message, pbf *pb.Fields, fields *ProtocolFields) { + if len(m.to) > 0 { + displayInfo, uri, params := parseFromToContact(m.to) + fields.ToDisplayInfo = displayInfo + fields.ToTag = params["tag"] + scheme, username, host, port, _ := parseURI(uri) + fields.ToURIOriginal = uri + fields.ToURIScheme = scheme + fields.ToURIHost = host + if !bytes.Equal(username, []byte(" ")) && !bytes.Equal(username, []byte("-")) { + fields.ToURIUsername = username + pbf.AddUser(string(username)) + } + fields.ToURIPort = port + pbf.AddHost(string(host)) + } +} + +func populateContactFields(m *message, pbf *pb.Fields, fields *ProtocolFields) { + if contact, found := m.headers["contact"]; found && len(contact) > 0 { + displayInfo, uri, params := parseFromToContact(m.to) + fields.ContactDisplayInfo = displayInfo + fields.ContactExpires, _ = strconv.Atoi(string(params["expires"])) + fields.ContactQ, _ = strconv.ParseFloat(string(params["q"]), 64) + scheme, username, host, port, urlparams := parseURI(uri) + fields.ContactURIOriginal = uri + fields.ContactURIScheme = scheme + fields.ContactURIHost = host + if !bytes.Equal(username, []byte(" ")) && !bytes.Equal(username, []byte("-")) { + fields.ContactURIUsername = username + pbf.AddUser(string(username)) + } + fields.ContactURIPort = port + fields.ContactLine = urlparams["line"] + fields.ContactTransport = bytes.ToLower(urlparams["transport"]) + pbf.AddHost(string(host)) + } +} + +func (p *plugin) populateEventFields(m *message, pbf *pb.Fields, fields ProtocolFields) { + pbf.Event.Kind = "event" + pbf.Event.Type = []string{"info"} + pbf.Event.Dataset = "sip" + pbf.Event.Sequence = int64(fields.CseqCode) + + // TODO: Get these values from body + pbf.Event.Start = m.ts + pbf.Event.End = m.ts + // + + if p.keepOriginal { + pbf.Event.Original = string(m.rawData) + } + + pbf.Event.Category = []string{"network", "protocol"} + if _, found := m.headers["authorization"]; found { + pbf.Event.Category = append(pbf.Event.Category, "authentication") + } + + pbf.Event.Action = func() string { + if m.isRequest { + return fmt.Sprintf("sip-%s", strings.ToLower(string(m.method))) + } + return fmt.Sprintf("sip-%s", strings.ToLower(string(fields.CseqMethod))) + }() + + pbf.Event.Outcome = func() string { + switch { + case m.statusCode < 200: + return "" + case m.statusCode > 299: + return "failure" + } + return "success" + }() + + pbf.Event.Reason = string(fields.Status) +} + +func populateAuthFields(m *message, evt beat.Event, pbf *pb.Fields, fields *ProtocolFields) { + auths, found := m.headers["authorization"] + if !found || len(auths) == 0 { + if isDetailed { + detailedf("sip packet without authorization header") + } + return + } + + if isDetailed { + detailedf("sip packet with authorization header") + } + + auth := bytes.TrimSpace(auths[0]) + pos := bytes.IndexByte(auth, ' ') + if pos == -1 { + if isDebug { + debugf("malformed authorization header: missing scheme") + } + return + } + + fields.AuthScheme = auth[:pos] + + pos += 1 + for _, param := range bytes.Split(auth[pos:], []byte(",")) { + kv := bytes.SplitN(param, []byte("="), 2) + if len(kv) != 2 { + continue + } + kv[1] = bytes.Trim(kv[1], "'\" \t") + switch string(bytes.ToLower(bytes.TrimSpace(kv[0]))) { + case "realm": + fields.AuthRealm = kv[1] + case "username": + username := string(kv[1]) + if username != "" && username != "-" { + _, _ = evt.Fields.Put("user.name", username) + pbf.AddUser(username) + } + case "uri": + scheme, _, host, port, _ := parseURI(kv[1]) + fields.AuthURIOriginal = kv[1] + fields.AuthURIScheme = scheme + fields.AuthURIHost = host + fields.AuthURIPort = port + } + } +} + +var constSDPContentType = []byte("application/sdp") + +func populateBodyFields(m *message, pbf *pb.Fields, fields *ProtocolFields) { + if !m.hasContentLength { + return + } + + if !bytes.Equal(m.contentType, constSDPContentType) { + if isDebug { + debugf("body content-type: %s is not supported", m.contentType) + } + return + } + + if _, found := m.headers["content-encoding"]; found { + if isDebug { + debugf("body decoding is not supported yet if content-endcoding is present") + } + return + } + + fields.SDPBodyOriginal = m.body + + var isInMedia bool + for _, line := range bytes.Split(m.body, []byte("\r\n")) { + kv := bytes.SplitN(line, []byte("="), 2) + if len(kv) != 2 { + continue + } + + kv[1] = bytes.TrimSpace(kv[1]) + ch := string(bytes.ToLower(bytes.TrimSpace(kv[0]))) + switch ch { + case "v": + fields.SDPVersion = string(kv[1]) + case "o": + var pos int + if kv[1][pos] == '"' { + endUserPos := bytes.IndexByte(kv[1][pos+1:], '"') + if !bytes.Equal(kv[1][pos+1:endUserPos], []byte("-")) { + fields.SDPOwnerUsername = kv[1][pos+1 : endUserPos] + } + pos = endUserPos + 1 + } + nParts := func() int { + if pos == 0 { + return 4 + } + return 3 // already have user + }() + parts := bytes.SplitN(kv[1][pos:], []byte(" "), nParts) + if len(parts) != nParts { + if isDebug { + debugf("malformed owner SDP line") + } + continue + } + if nParts == 4 { + if !bytes.Equal(parts[0], []byte("-")) { + fields.SDPOwnerUsername = parts[0] + } + parts = parts[1:] + } + fields.SDPOwnerSessID = parts[0] + fields.SDPOwnerVersion = parts[1] + fields.SDPOwnerIP = func() common.NetString { + p := bytes.Split(parts[2], []byte(" ")) + return p[len(p)-1] + }() + pbf.AddUser(string(fields.SDPOwnerUsername)) + pbf.AddIP(string(fields.SDPOwnerIP)) + case "s": + if !bytes.Equal(kv[1], []byte("-")) { + fields.SDPSessName = kv[1] + } + case "c": + if isInMedia { + continue + } + fields.SDPConnInfo = kv[1] + fields.SDPConnAddr = func() common.NetString { + p := bytes.Split(kv[1], []byte(" ")) + return p[len(p)-1] + }() + pbf.AddHost(string(fields.SDPConnAddr)) + case "m": + isInMedia = true + // TODO + case "i", "u", "e", "p", "b", "t", "r", "z", "k", "a": + // TODO + } + } +} + +func parseFromToContact(fromTo common.NetString) (displayInfo, uri common.NetString, params map[string]common.NetString) { + params = make(map[string]common.NetString) + + pos := bytes.IndexByte(fromTo, '<') + if pos == -1 { + pos = bytes.IndexByte(fromTo, ' ') + } + + displayInfo = bytes.Trim(fromTo[:pos], "'\"\t ") + + endURIPos := func() int { + if fromTo[pos] == '<' { + return bytes.IndexByte(fromTo, '>') + } + return bytes.IndexByte(fromTo, ';') + }() + + if endURIPos == -1 { + uri = bytes.TrimRight(fromTo[pos:], ">") + return + } + pos += 1 + uri = fromTo[pos:endURIPos] + + pos = endURIPos + 1 + for _, param := range bytes.Split(fromTo[pos:], []byte(";")) { + kv := bytes.SplitN(param, []byte("="), 2) + if len(kv) != 2 { + continue + } + params[string(bytes.ToLower(bytes.TrimSpace(kv[0])))] = kv[1] + } + + return displayInfo, uri, params +} + +func parseURI(uri common.NetString) (scheme, username, host common.NetString, port int, params map[string]common.NetString) { + var ( + prevChar rune + inIPv6 bool + idx int + hasParams bool + ) + uri = bytes.TrimSpace(uri) + prevChar = ' ' + pos := -1 + ppos := -1 + epos := len(uri) + + params = make(map[string]common.NetString) +loop: + for idx = 0; idx < len(uri); idx++ { + curChar := rune(uri[idx]) + + switch { + case idx == 0: + colonIdx := bytes.Index(uri, []byte(":")) + if colonIdx == -1 { + break loop + } + scheme = uri[:colonIdx] + idx += colonIdx + pos = idx + 1 + + case curChar == '[' && prevChar != '\\': + inIPv6 = true + + case curChar == ']' && prevChar != '\\': + inIPv6 = false + + case curChar == ';' && prevChar != '\\': + // we found end of URI + hasParams = true + epos = idx + break loop + + default: + // select wich part + switch curChar { + case '@': + if len(host) > 0 { + pos = ppos + host = nil + } + username = uri[pos:idx] + ppos = pos + pos = idx + 1 + case ':': + if !inIPv6 { + host = uri[pos:idx] + ppos = pos + pos = idx + 1 + } + } + } + + prevChar = curChar + } + + if pos > 0 && epos <= len(uri) && pos <= epos { + if len(host) == 0 { + host = bytes.TrimSpace(uri[pos:epos]) + } else { + port, _ = strconv.Atoi(string(bytes.TrimSpace(uri[pos:epos]))) + } + } + + if hasParams { + for _, param := range bytes.Split(uri[epos+1:], []byte(";")) { + kv := bytes.Split(param, []byte("=")) + if len(kv) != 2 { + continue + } + params[string(bytes.ToLower(bytes.TrimSpace(kv[0])))] = kv[1] + } + } + + return scheme, username, host, port, params +} diff --git a/packetbeat/protos/sip/plugin_test.go b/packetbeat/protos/sip/plugin_test.go new file mode 100644 index 00000000000..fc9ee53aff2 --- /dev/null +++ b/packetbeat/protos/sip/plugin_test.go @@ -0,0 +1,168 @@ +// 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. + +// +build !integration + +package sip + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/elastic/beats/v7/libbeat/beat" + "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/packetbeat/protos" +) + +func TestParseURI(t *testing.T) { + scheme, username, host, port, params := parseURI(common.NetString("sip:test@10.0.2.15:5060")) + assert.Equal(t, common.NetString("sip"), scheme) + assert.Equal(t, common.NetString("test"), username) + assert.Equal(t, common.NetString("10.0.2.15"), host) + assert.Equal(t, map[string]common.NetString{}, params) + assert.Equal(t, 5060, port) + + scheme, username, host, port, params = parseURI(common.NetString("sips:test@10.0.2.15:5061 ; transport=udp")) + assert.Equal(t, common.NetString("sips"), scheme) + assert.Equal(t, common.NetString("test"), username) + assert.Equal(t, common.NetString("10.0.2.15"), host) + assert.Equal(t, common.NetString("udp"), params["transport"]) + assert.Equal(t, 5061, port) + + scheme, username, host, port, params = parseURI(common.NetString("mailto:192.168.0.2")) + assert.Equal(t, common.NetString("mailto"), scheme) + assert.Equal(t, common.NetString(nil), username) + assert.Equal(t, common.NetString("192.168.0.2"), host) + assert.Equal(t, map[string]common.NetString{}, params) + assert.Equal(t, 0, port) +} + +func TestParseFromTo(t *testing.T) { + // To + displayInfo, uri, params := parseFromToContact(common.NetString("test ;tag=QvN921")) + assert.Equal(t, common.NetString("test"), displayInfo) + assert.Equal(t, common.NetString("sip:test@10.0.2.15:5060"), uri) + assert.Equal(t, common.NetString("QvN921"), params["tag"]) + displayInfo, uri, params = parseFromToContact(common.NetString("test ")) + assert.Equal(t, common.NetString("test"), displayInfo) + assert.Equal(t, common.NetString("sip:test@10.0.2.15:5060"), uri) + assert.Equal(t, common.NetString(nil), params["tag"]) + + // From + displayInfo, uri, params = parseFromToContact(common.NetString("\"PCMU/8000\" ;tag=1")) + assert.Equal(t, common.NetString("PCMU/8000"), displayInfo) + assert.Equal(t, common.NetString("sip:sipp@10.0.2.15:5060"), uri) + assert.Equal(t, common.NetString("1"), params["tag"]) + displayInfo, uri, params = parseFromToContact(common.NetString("\"Matthew Hodgson\" ;tag=5c7cdb68")) + assert.Equal(t, common.NetString("Matthew Hodgson"), displayInfo) + assert.Equal(t, common.NetString("sip:matthew@mxtelecom.com"), uri) + assert.Equal(t, common.NetString("5c7cdb68"), params["tag"]) + displayInfo, uri, params = parseFromToContact(common.NetString(";tag=5c7cdb68")) + assert.Equal(t, common.NetString(nil), displayInfo) + assert.Equal(t, common.NetString("sip:matthew@mxtelecom.com"), uri) + assert.Equal(t, common.NetString("5c7cdb68"), params["tag"]) + displayInfo, uri, params = parseFromToContact(common.NetString("")) + assert.Equal(t, common.NetString(nil), displayInfo) + assert.Equal(t, common.NetString("sip:matthew@mxtelecom.com"), uri) + assert.Equal(t, common.NetString(nil), params["tag"]) + + // Contact + displayInfo, uri, _ = parseFromToContact(common.NetString("")) + assert.Equal(t, common.NetString(nil), displayInfo) + assert.Equal(t, common.NetString("sip:test@10.0.2.15:5060;transport=udp"), uri) + displayInfo, uri, params = parseFromToContact(common.NetString(";expires=1200;q=0.500")) + assert.Equal(t, common.NetString(nil), displayInfo) + assert.Equal(t, common.NetString("sip:voi18062@192.168.1.2:5060;line=aca6b97ca3f5e51a"), uri) + assert.Equal(t, common.NetString("1200"), params["expires"]) + assert.Equal(t, common.NetString("0.500"), params["q"]) + displayInfo, uri, params = parseFromToContact(common.NetString(" \"Mr. Watson\" ;q=0.7; expires=3600")) + assert.Equal(t, common.NetString("Mr. Watson"), displayInfo) + assert.Equal(t, common.NetString("sip:watson@worcester.bell-telephone.com"), uri) + assert.Equal(t, common.NetString("3600"), params["expires"]) + assert.Equal(t, common.NetString("0.7"), params["q"]) + displayInfo, uri, params = parseFromToContact(common.NetString(" \"Mr. Watson\" ;q=0.1")) + assert.Equal(t, common.NetString("Mr. Watson"), displayInfo) + assert.Equal(t, common.NetString("mailto:watson@bell-telephone.com"), uri) + assert.Equal(t, common.NetString("0.1"), params["q"]) +} + +func TestParseUDP(t *testing.T) { + gotEvent := new(beat.Event) + reporter := func(evt beat.Event) { + gotEvent = &evt + } + const data = "INVITE sip:test@10.0.2.15:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 10.0.2.20:5060;branch=z9hG4bK-2187-1-0\r\nFrom: \"DVI4/8000\" ;tag=1\r\nTo: test \r\nCall-ID: 1-2187@10.0.2.20\r\nCSeq: 1 INVITE\r\nContact: sip:sipp@10.0.2.20:5060\r\nMax-Forwards: 70\r\nContent-Type: application/sdp\r\nContent-Length: 123\r\n\r\nv=0\r\no=- 42 42 IN IP4 10.0.2.20\r\ns=-\r\nc=IN IP4 10.0.2.20\r\nt=0 0\r\nm=audio 6000 RTP/AVP 5\r\na=rtpmap:5 DVI4/8000\r\na=recvonly\r\n" + p, _ := New(true, reporter, nil) + plugin := p.(*plugin) + plugin.ParseUDP(&protos.Packet{ + Ts: time.Now(), + Tuple: common.IPPortTuple{}, + Payload: []byte(data), + }) + fields := *gotEvent + + assert.Equal(t, common.NetString("1-2187@10.0.2.20"), getVal(fields, "sip.call_id")) + assert.Equal(t, common.NetString("test"), getVal(fields, "sip.contact.display_info")) + assert.Equal(t, common.NetString("10.0.2.15"), getVal(fields, "sip.contact.uri.host")) + assert.Equal(t, common.NetString("sip:test@10.0.2.15:5060"), getVal(fields, "sip.contact.uri.original")) + assert.Equal(t, 5060, getVal(fields, "sip.contact.uri.port")) + assert.Equal(t, common.NetString("sip"), getVal(fields, "sip.contact.uri.scheme")) + assert.Equal(t, common.NetString("test"), getVal(fields, "sip.contact.uri.username")) + assert.Equal(t, 123, getVal(fields, "sip.content_length")) + assert.Equal(t, common.NetString("application/sdp"), getVal(fields, "sip.content_type")) + assert.Equal(t, 1, getVal(fields, "sip.cseq.code")) + assert.Equal(t, common.NetString("INVITE"), getVal(fields, "sip.cseq.method")) + assert.Equal(t, common.NetString("DVI4/8000"), getVal(fields, "sip.from.display_info")) + assert.Equal(t, common.NetString("1"), getVal(fields, "sip.from.tag")) + assert.Equal(t, common.NetString("10.0.2.20"), getVal(fields, "sip.from.uri.host")) + assert.Equal(t, common.NetString("sip:sipp@10.0.2.20:5060"), getVal(fields, "sip.from.uri.original")) + assert.Equal(t, 5060, getVal(fields, "sip.from.uri.port")) + assert.Equal(t, common.NetString("sip"), getVal(fields, "sip.from.uri.scheme")) + assert.Equal(t, common.NetString("sipp"), getVal(fields, "sip.from.uri.username")) + assert.Equal(t, 70, getVal(fields, "sip.max_forwards")) + assert.Equal(t, common.NetString("INVITE"), getVal(fields, "sip.method")) + assert.Equal(t, common.NetString("10.0.2.20"), getVal(fields, "sip.sdp.connection.address")) + assert.Equal(t, common.NetString("IN IP4 10.0.2.20"), getVal(fields, "sip.sdp.connection.info")) + assert.Equal(t, common.NetString("10.0.2.20"), getVal(fields, "sip.sdp.owner.ip")) + assert.Equal(t, common.NetString("42"), getVal(fields, "sip.sdp.owner.session_id")) + assert.Equal(t, common.NetString("42"), getVal(fields, "sip.sdp.owner.version")) + assert.Equal(t, nil, getVal(fields, "sip.sdp.owner.username")) + assert.Equal(t, nil, getVal(fields, "sip.sdp.session.name")) + assert.Equal(t, "0", getVal(fields, "sip.sdp.version")) + assert.Equal(t, common.NetString("test"), getVal(fields, "sip.to.display_info")) + assert.Equal(t, nil, getVal(fields, "sip.to.tag")) + assert.Equal(t, common.NetString("10.0.2.15"), getVal(fields, "sip.to.uri.host")) + assert.Equal(t, common.NetString("sip:test@10.0.2.15:5060"), getVal(fields, "sip.to.uri.original")) + assert.Equal(t, 5060, getVal(fields, "sip.to.uri.port")) + assert.Equal(t, common.NetString("sip"), getVal(fields, "sip.to.uri.scheme")) + assert.Equal(t, common.NetString("test"), getVal(fields, "sip.to.uri.username")) + assert.Equal(t, "request", getVal(fields, "sip.type")) + assert.Equal(t, common.NetString("10.0.2.15"), getVal(fields, "sip.uri.host")) + assert.Equal(t, common.NetString("sip:test@10.0.2.15:5060"), getVal(fields, "sip.uri.original")) + assert.Equal(t, 5060, getVal(fields, "sip.uri.port")) + assert.Equal(t, common.NetString("sip"), getVal(fields, "sip.uri.scheme")) + assert.Equal(t, common.NetString("test"), getVal(fields, "sip.uri.username")) + assert.Equal(t, "2.0", getVal(fields, "sip.version")) + assert.EqualValues(t, []common.NetString{common.NetString("SIP/2.0/UDP 10.0.2.20:5060;branch=z9hG4bK-2187-1-0")}, getVal(fields, "sip.via.original")) +} + +func getVal(f beat.Event, k string) interface{} { + v, _ := f.GetValue(k) + return v +} diff --git a/packetbeat/tests/system/config/golden-tests.yml b/packetbeat/tests/system/config/golden-tests.yml index 6d49ce9c221..42ad0c746d2 100644 --- a/packetbeat/tests/system/config/golden-tests.yml +++ b/packetbeat/tests/system/config/golden-tests.yml @@ -27,3 +27,11 @@ test_cases: - name: TLS 1.3 pcap: pcaps/tls-version-13.pcap config: {} + + - name: SIP + pcap: pcaps/sip.pcap + config: {} + + - name: SIP Authenticated Register + pcap: pcaps/sip_authenticated_register.pcap + config: {} diff --git a/packetbeat/tests/system/config/packetbeat.yml.j2 b/packetbeat/tests/system/config/packetbeat.yml.j2 index b687f6f0402..7b253d8ec2c 100644 --- a/packetbeat/tests/system/config/packetbeat.yml.j2 +++ b/packetbeat/tests/system/config/packetbeat.yml.j2 @@ -148,6 +148,9 @@ packetbeat.protocols: {% if mongodb_max_docs is not none %} max_docs: {{mongodb_max_docs}}{% endif %} {% if mongodb_max_doc_length is not none %} max_doc_length: {{mongodb_max_doc_length}}{% endif %} +- type: sip + ports: [{{ sip_ports|default([5060])|join(", ") }}] + {% if procs_enabled %} #=========================== Monitored processes ============================== diff --git a/packetbeat/tests/system/golden/sip-expected.json b/packetbeat/tests/system/golden/sip-expected.json new file mode 100644 index 00000000000..37d95d715a7 --- /dev/null +++ b/packetbeat/tests/system/golden/sip-expected.json @@ -0,0 +1,668 @@ +[ + { + "@metadata.beat": "packetbeat", + "@metadata.type": "_doc", + "client.ip": "10.0.2.20", + "client.port": 5060, + "destination.ip": "10.0.2.15", + "destination.port": 5060, + "event.action": "sip-invite", + "event.category": [ + "network", + "protocol" + ], + "event.dataset": "sip", + "event.duration": 0, + "event.kind": "event", + "event.original": "INVITE sip:test@10.0.2.15:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 10.0.2.20:5060;branch=z9hG4bK-2187-1-0\r\nFrom: \"DVI4/8000\" ;tag=1\r\nTo: test \r\nCall-ID: 1-2187@10.0.2.20\r\nCSeq: 1 INVITE\r\nContact: sip:sipp@10.0.2.20:5060\r\nMax-Forwards: 70\r\nContent-Type: application/sdp\r\nContent-Length: 123\r\n\r\nv=0\r\no=- 42 42 IN IP4 10.0.2.20\r\ns=-\r\nc=IN IP4 10.0.2.20\r\nt=0 0\r\nm=audio 6000 RTP/AVP 5\r\na=rtpmap:5 DVI4/8000\r\na=recvonly\r\n", + "event.sequence": 1, + "event.type": [ + "info" + ], + "network.application": "sip", + "network.community_id": "1:xDRQZvk3ErEhBDslXv1c6EKI804=", + "network.iana_number": "17", + "network.protocol": "sip", + "network.transport": "udp", + "network.type": "ipv4", + "related.hosts": [ + "10.0.2.15", + "10.0.2.20" + ], + "related.ip": [ + "10.0.2.20", + "10.0.2.15" + ], + "related.user": [ + "test", + "sipp" + ], + "server.ip": "10.0.2.15", + "server.port": 5060, + "sip.call_id": "1-2187@10.0.2.20", + "sip.contact.display_info": "test", + "sip.contact.uri.host": "10.0.2.15", + "sip.contact.uri.original": "sip:test@10.0.2.15:5060", + "sip.contact.uri.port": 5060, + "sip.contact.uri.scheme": "sip", + "sip.contact.uri.username": "test", + "sip.content_length": 123, + "sip.content_type": "application/sdp", + "sip.cseq.code": 1, + "sip.cseq.method": "INVITE", + "sip.from.display_info": "DVI4/8000", + "sip.from.tag": "1", + "sip.from.uri.host": "10.0.2.20", + "sip.from.uri.original": "sip:sipp@10.0.2.20:5060", + "sip.from.uri.port": 5060, + "sip.from.uri.scheme": "sip", + "sip.from.uri.username": "sipp", + "sip.max_forwards": 70, + "sip.method": "INVITE", + "sip.sdp.body.original": "v=0\r\no=- 42 42 IN IP4 10.0.2.20\r\ns=-\r\nc=IN IP4 10.0.2.20\r\nt=0 0\r\nm=audio 6000 RTP/AVP 5\r\na=rtpmap:5 DVI4/8000\r\na=recvonly\r\n", + "sip.sdp.connection.address": "10.0.2.20", + "sip.sdp.connection.info": "IN IP4 10.0.2.20", + "sip.sdp.owner.ip": "10.0.2.20", + "sip.sdp.owner.session_id": "42", + "sip.sdp.owner.version": "42", + "sip.sdp.version": "0", + "sip.to.display_info": "test", + "sip.to.uri.host": "10.0.2.15", + "sip.to.uri.original": "sip:test@10.0.2.15:5060", + "sip.to.uri.port": 5060, + "sip.to.uri.scheme": "sip", + "sip.to.uri.username": "test", + "sip.type": "request", + "sip.uri.host": "10.0.2.15", + "sip.uri.original": "sip:test@10.0.2.15:5060", + "sip.uri.port": 5060, + "sip.uri.scheme": "sip", + "sip.uri.username": "test", + "sip.version": "2.0", + "sip.via.original": [ + "SIP/2.0/UDP 10.0.2.20:5060;branch=z9hG4bK-2187-1-0" + ], + "source.ip": "10.0.2.20", + "source.port": 5060, + "status": "OK", + "type": "sip" + }, + { + "@metadata.beat": "packetbeat", + "@metadata.type": "_doc", + "client.ip": "10.0.2.15", + "client.port": 5060, + "destination.ip": "10.0.2.20", + "destination.port": 5060, + "event.action": "sip-invite", + "event.category": [ + "network", + "protocol" + ], + "event.dataset": "sip", + "event.duration": 0, + "event.kind": "event", + "event.original": "SIP/2.0 100 Trying\r\nVia: SIP/2.0/UDP 10.0.2.20:5060;branch=z9hG4bK-2187-1-0\r\nFrom: \"DVI4/8000\" ;tag=1\r\nTo: test \r\nCall-ID: 1-2187@10.0.2.20\r\nCSeq: 1 INVITE\r\nUser-Agent: FreeSWITCH-mod_sofia/1.6.12-20-b91a0a6~64bit\r\nContent-Length: 0\r\n\r\n", + "event.reason": "Trying", + "event.sequence": 1, + "event.type": [ + "info" + ], + "network.application": "sip", + "network.community_id": "1:xDRQZvk3ErEhBDslXv1c6EKI804=", + "network.iana_number": "17", + "network.protocol": "sip", + "network.transport": "udp", + "network.type": "ipv4", + "related.hosts": [ + "10.0.2.20", + "10.0.2.15" + ], + "related.ip": [ + "10.0.2.15", + "10.0.2.20" + ], + "related.user": [ + "sipp", + "test" + ], + "server.ip": "10.0.2.20", + "server.port": 5060, + "sip.call_id": "1-2187@10.0.2.20", + "sip.code": 100, + "sip.cseq.code": 1, + "sip.cseq.method": "INVITE", + "sip.from.display_info": "DVI4/8000", + "sip.from.tag": "1", + "sip.from.uri.host": "10.0.2.20", + "sip.from.uri.original": "sip:sipp@10.0.2.20:5060", + "sip.from.uri.port": 5060, + "sip.from.uri.scheme": "sip", + "sip.from.uri.username": "sipp", + "sip.status": "Trying", + "sip.to.display_info": "test", + "sip.to.uri.host": "10.0.2.15", + "sip.to.uri.original": "sip:test@10.0.2.15:5060", + "sip.to.uri.port": 5060, + "sip.to.uri.scheme": "sip", + "sip.to.uri.username": "test", + "sip.type": "response", + "sip.user_agent.original": "FreeSWITCH-mod_sofia/1.6.12-20-b91a0a6~64bit", + "sip.version": "2.0", + "sip.via.original": [ + "SIP/2.0/UDP 10.0.2.20:5060;branch=z9hG4bK-2187-1-0" + ], + "source.ip": "10.0.2.15", + "source.port": 5060, + "status": "OK", + "type": "sip" + }, + { + "@metadata.beat": "packetbeat", + "@metadata.type": "_doc", + "client.ip": "10.0.2.20", + "client.port": 5060, + "destination.ip": "10.0.2.15", + "destination.port": 5060, + "event.action": "sip-ack", + "event.category": [ + "network", + "protocol" + ], + "event.dataset": "sip", + "event.duration": 0, + "event.kind": "event", + "event.original": "ACK sip:test@10.0.2.15:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 10.0.2.20:5060;branch=z9hG4bK-2187-1-5\r\nFrom: \"DVI4/8000\" ;tag=1\r\nTo: test ;tag=e2jv529vDZ3eQ\r\nCall-ID: 1-2187@10.0.2.20\r\nCSeq: 1 ACK\r\nContact: sip:sipp@10.0.2.20:5060\r\nMax-Forwards: 70\r\nContent-Length: 0\r\n\r\n", + "event.sequence": 1, + "event.type": [ + "info" + ], + "network.application": "sip", + "network.community_id": "1:xDRQZvk3ErEhBDslXv1c6EKI804=", + "network.iana_number": "17", + "network.protocol": "sip", + "network.transport": "udp", + "network.type": "ipv4", + "related.hosts": [ + "10.0.2.15", + "10.0.2.20" + ], + "related.ip": [ + "10.0.2.20", + "10.0.2.15" + ], + "related.user": [ + "test", + "sipp" + ], + "server.ip": "10.0.2.15", + "server.port": 5060, + "sip.call_id": "1-2187@10.0.2.20", + "sip.contact.display_info": "test", + "sip.contact.uri.host": "10.0.2.15", + "sip.contact.uri.original": "sip:test@10.0.2.15:5060", + "sip.contact.uri.port": 5060, + "sip.contact.uri.scheme": "sip", + "sip.contact.uri.username": "test", + "sip.cseq.code": 1, + "sip.cseq.method": "ACK", + "sip.from.display_info": "DVI4/8000", + "sip.from.tag": "1", + "sip.from.uri.host": "10.0.2.20", + "sip.from.uri.original": "sip:sipp@10.0.2.20:5060", + "sip.from.uri.port": 5060, + "sip.from.uri.scheme": "sip", + "sip.from.uri.username": "sipp", + "sip.max_forwards": 70, + "sip.method": "ACK", + "sip.to.display_info": "test", + "sip.to.tag": "e2jv529vDZ3eQ", + "sip.to.uri.host": "10.0.2.15", + "sip.to.uri.original": "sip:test@10.0.2.15:5060", + "sip.to.uri.port": 5060, + "sip.to.uri.scheme": "sip", + "sip.to.uri.username": "test", + "sip.type": "request", + "sip.uri.host": "10.0.2.15", + "sip.uri.original": "sip:test@10.0.2.15:5060", + "sip.uri.port": 5060, + "sip.uri.scheme": "sip", + "sip.uri.username": "test", + "sip.version": "2.0", + "sip.via.original": [ + "SIP/2.0/UDP 10.0.2.20:5060;branch=z9hG4bK-2187-1-5" + ], + "source.ip": "10.0.2.20", + "source.port": 5060, + "status": "OK", + "type": "sip" + }, + { + "@metadata.beat": "packetbeat", + "@metadata.type": "_doc", + "client.ip": "10.0.2.15", + "client.port": 5060, + "destination.ip": "10.0.2.20", + "destination.port": 5060, + "event.action": "sip-bye", + "event.category": [ + "network", + "protocol" + ], + "event.dataset": "sip", + "event.duration": 0, + "event.kind": "event", + "event.original": "BYE sip:sipp@10.0.2.20:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 10.0.2.15;rport;branch=z9hG4bKDQ7XK6BBH57ya\r\nMax-Forwards: 70\r\nFrom: test ;tag=e2jv529vDZ3eQ\r\nTo: \"DVI4/8000\" ;tag=1\r\nCall-ID: 1-2187@10.0.2.20\r\nCSeq: 99750433 BYE\r\nUser-Agent: FreeSWITCH-mod_sofia/1.6.12-20-b91a0a6~64bit\r\nAllow: INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, INFO, UPDATE, REGISTER, REFER, NOTIFY, PUBLISH, SUBSCRIBE\r\nSupported: timer, path, replaces\r\nReason: Q.850;cause=16;text=\"NORMAL_CLEARING\"\r\nContent-Length: 0\r\n\r\n", + "event.sequence": 99750433, + "event.type": [ + "info" + ], + "network.application": "sip", + "network.community_id": "1:xDRQZvk3ErEhBDslXv1c6EKI804=", + "network.iana_number": "17", + "network.protocol": "sip", + "network.transport": "udp", + "network.type": "ipv4", + "related.hosts": [ + "10.0.2.20", + "10.0.2.15" + ], + "related.ip": [ + "10.0.2.15", + "10.0.2.20" + ], + "related.user": [ + "sipp", + "test" + ], + "server.ip": "10.0.2.20", + "server.port": 5060, + "sip.allow": [ + "invite", + "ack", + "bye", + "cancel", + "options", + "message", + "info", + "update", + "register", + "refer", + "notify", + "publish", + "subscribe" + ], + "sip.call_id": "1-2187@10.0.2.20", + "sip.cseq.code": 99750433, + "sip.cseq.method": "BYE", + "sip.from.display_info": "test", + "sip.from.tag": "e2jv529vDZ3eQ", + "sip.from.uri.host": "10.0.2.15", + "sip.from.uri.original": "sip:test@10.0.2.15:5060", + "sip.from.uri.port": 5060, + "sip.from.uri.scheme": "sip", + "sip.from.uri.username": "test", + "sip.max_forwards": 70, + "sip.method": "BYE", + "sip.supported": [ + "timer", + "path", + "replaces" + ], + "sip.to.display_info": "DVI4/8000", + "sip.to.tag": "1", + "sip.to.uri.host": "10.0.2.20", + "sip.to.uri.original": "sip:sipp@10.0.2.20:5060", + "sip.to.uri.port": 5060, + "sip.to.uri.scheme": "sip", + "sip.to.uri.username": "sipp", + "sip.type": "request", + "sip.uri.host": "10.0.2.20", + "sip.uri.original": "sip:sipp@10.0.2.20:5060", + "sip.uri.port": 5060, + "sip.uri.scheme": "sip", + "sip.uri.username": "sipp", + "sip.user_agent.original": "FreeSWITCH-mod_sofia/1.6.12-20-b91a0a6~64bit", + "sip.version": "2.0", + "sip.via.original": [ + "SIP/2.0/UDP 10.0.2.15;rport;branch=z9hG4bKDQ7XK6BBH57ya" + ], + "source.ip": "10.0.2.15", + "source.port": 5060, + "status": "OK", + "type": "sip" + }, + { + "@metadata.beat": "packetbeat", + "@metadata.type": "_doc", + "client.ip": "10.0.2.20", + "client.port": 5060, + "destination.ip": "10.0.2.15", + "destination.port": 5060, + "event.action": "sip-invite", + "event.category": [ + "network", + "protocol" + ], + "event.dataset": "sip", + "event.duration": 0, + "event.kind": "event", + "event.original": "INVITE sip:test@10.0.2.15:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 10.0.2.20:5060;branch=z9hG4bK-2189-1-0\r\nFrom: \"DVI4/16000\" ;tag=1\r\nTo: test \r\nCall-ID: 1-2189@10.0.2.20\r\nCSeq: 1 INVITE\r\nContact: sip:sipp@10.0.2.20:5060\r\nMax-Forwards: 70\r\nContent-Type: application/sdp\r\nContent-Length: 124\r\n\r\nv=0\r\no=- 42 42 IN IP4 10.0.2.20\r\ns=-\r\nc=IN IP4 10.0.2.20\r\nt=0 0\r\nm=audio 6000 RTP/AVP 6\r\na=rtpmap:6 DVI4/16000\r\na=recvonly\r\n", + "event.sequence": 1, + "event.type": [ + "info" + ], + "network.application": "sip", + "network.community_id": "1:xDRQZvk3ErEhBDslXv1c6EKI804=", + "network.iana_number": "17", + "network.protocol": "sip", + "network.transport": "udp", + "network.type": "ipv4", + "related.hosts": [ + "10.0.2.15", + "10.0.2.20" + ], + "related.ip": [ + "10.0.2.20", + "10.0.2.15" + ], + "related.user": [ + "test", + "sipp" + ], + "server.ip": "10.0.2.15", + "server.port": 5060, + "sip.call_id": "1-2189@10.0.2.20", + "sip.contact.display_info": "test", + "sip.contact.uri.host": "10.0.2.15", + "sip.contact.uri.original": "sip:test@10.0.2.15:5060", + "sip.contact.uri.port": 5060, + "sip.contact.uri.scheme": "sip", + "sip.contact.uri.username": "test", + "sip.content_length": 124, + "sip.content_type": "application/sdp", + "sip.cseq.code": 1, + "sip.cseq.method": "INVITE", + "sip.from.display_info": "DVI4/16000", + "sip.from.tag": "1", + "sip.from.uri.host": "10.0.2.20", + "sip.from.uri.original": "sip:sipp@10.0.2.20:5060", + "sip.from.uri.port": 5060, + "sip.from.uri.scheme": "sip", + "sip.from.uri.username": "sipp", + "sip.max_forwards": 70, + "sip.method": "INVITE", + "sip.sdp.body.original": "v=0\r\no=- 42 42 IN IP4 10.0.2.20\r\ns=-\r\nc=IN IP4 10.0.2.20\r\nt=0 0\r\nm=audio 6000 RTP/AVP 6\r\na=rtpmap:6 DVI4/16000\r\na=recvonly\r\n", + "sip.sdp.connection.address": "10.0.2.20", + "sip.sdp.connection.info": "IN IP4 10.0.2.20", + "sip.sdp.owner.ip": "10.0.2.20", + "sip.sdp.owner.session_id": "42", + "sip.sdp.owner.version": "42", + "sip.sdp.version": "0", + "sip.to.display_info": "test", + "sip.to.uri.host": "10.0.2.15", + "sip.to.uri.original": "sip:test@10.0.2.15:5060", + "sip.to.uri.port": 5060, + "sip.to.uri.scheme": "sip", + "sip.to.uri.username": "test", + "sip.type": "request", + "sip.uri.host": "10.0.2.15", + "sip.uri.original": "sip:test@10.0.2.15:5060", + "sip.uri.port": 5060, + "sip.uri.scheme": "sip", + "sip.uri.username": "test", + "sip.version": "2.0", + "sip.via.original": [ + "SIP/2.0/UDP 10.0.2.20:5060;branch=z9hG4bK-2189-1-0" + ], + "source.ip": "10.0.2.20", + "source.port": 5060, + "status": "OK", + "type": "sip" + }, + { + "@metadata.beat": "packetbeat", + "@metadata.type": "_doc", + "client.ip": "10.0.2.15", + "client.port": 5060, + "destination.ip": "10.0.2.20", + "destination.port": 5060, + "event.action": "sip-invite", + "event.category": [ + "network", + "protocol" + ], + "event.dataset": "sip", + "event.duration": 0, + "event.kind": "event", + "event.original": "SIP/2.0 100 Trying\r\nVia: SIP/2.0/UDP 10.0.2.20:5060;branch=z9hG4bK-2189-1-0\r\nFrom: \"DVI4/16000\" ;tag=1\r\nTo: test \r\nCall-ID: 1-2189@10.0.2.20\r\nCSeq: 1 INVITE\r\nUser-Agent: FreeSWITCH-mod_sofia/1.6.12-20-b91a0a6~64bit\r\nContent-Length: 0\r\n\r\n", + "event.reason": "Trying", + "event.sequence": 1, + "event.type": [ + "info" + ], + "network.application": "sip", + "network.community_id": "1:xDRQZvk3ErEhBDslXv1c6EKI804=", + "network.iana_number": "17", + "network.protocol": "sip", + "network.transport": "udp", + "network.type": "ipv4", + "related.hosts": [ + "10.0.2.20", + "10.0.2.15" + ], + "related.ip": [ + "10.0.2.15", + "10.0.2.20" + ], + "related.user": [ + "sipp", + "test" + ], + "server.ip": "10.0.2.20", + "server.port": 5060, + "sip.call_id": "1-2189@10.0.2.20", + "sip.code": 100, + "sip.cseq.code": 1, + "sip.cseq.method": "INVITE", + "sip.from.display_info": "DVI4/16000", + "sip.from.tag": "1", + "sip.from.uri.host": "10.0.2.20", + "sip.from.uri.original": "sip:sipp@10.0.2.20:5060", + "sip.from.uri.port": 5060, + "sip.from.uri.scheme": "sip", + "sip.from.uri.username": "sipp", + "sip.status": "Trying", + "sip.to.display_info": "test", + "sip.to.uri.host": "10.0.2.15", + "sip.to.uri.original": "sip:test@10.0.2.15:5060", + "sip.to.uri.port": 5060, + "sip.to.uri.scheme": "sip", + "sip.to.uri.username": "test", + "sip.type": "response", + "sip.user_agent.original": "FreeSWITCH-mod_sofia/1.6.12-20-b91a0a6~64bit", + "sip.version": "2.0", + "sip.via.original": [ + "SIP/2.0/UDP 10.0.2.20:5060;branch=z9hG4bK-2189-1-0" + ], + "source.ip": "10.0.2.15", + "source.port": 5060, + "status": "OK", + "type": "sip" + }, + { + "@metadata.beat": "packetbeat", + "@metadata.type": "_doc", + "client.ip": "10.0.2.20", + "client.port": 5060, + "destination.ip": "10.0.2.15", + "destination.port": 5060, + "event.action": "sip-ack", + "event.category": [ + "network", + "protocol" + ], + "event.dataset": "sip", + "event.duration": 0, + "event.kind": "event", + "event.original": "ACK sip:test@10.0.2.15:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 10.0.2.20:5060;branch=z9hG4bK-2189-1-5\r\nFrom: \"DVI4/16000\" ;tag=1\r\nTo: test ;tag=FBcN7Xt0a8S1j\r\nCall-ID: 1-2189@10.0.2.20\r\nCSeq: 1 ACK\r\nContact: sip:sipp@10.0.2.20:5060\r\nMax-Forwards: 70\r\nContent-Length: 0\r\n\r\n", + "event.sequence": 1, + "event.type": [ + "info" + ], + "network.application": "sip", + "network.community_id": "1:xDRQZvk3ErEhBDslXv1c6EKI804=", + "network.iana_number": "17", + "network.protocol": "sip", + "network.transport": "udp", + "network.type": "ipv4", + "related.hosts": [ + "10.0.2.15", + "10.0.2.20" + ], + "related.ip": [ + "10.0.2.20", + "10.0.2.15" + ], + "related.user": [ + "test", + "sipp" + ], + "server.ip": "10.0.2.15", + "server.port": 5060, + "sip.call_id": "1-2189@10.0.2.20", + "sip.contact.display_info": "test", + "sip.contact.uri.host": "10.0.2.15", + "sip.contact.uri.original": "sip:test@10.0.2.15:5060", + "sip.contact.uri.port": 5060, + "sip.contact.uri.scheme": "sip", + "sip.contact.uri.username": "test", + "sip.cseq.code": 1, + "sip.cseq.method": "ACK", + "sip.from.display_info": "DVI4/16000", + "sip.from.tag": "1", + "sip.from.uri.host": "10.0.2.20", + "sip.from.uri.original": "sip:sipp@10.0.2.20:5060", + "sip.from.uri.port": 5060, + "sip.from.uri.scheme": "sip", + "sip.from.uri.username": "sipp", + "sip.max_forwards": 70, + "sip.method": "ACK", + "sip.to.display_info": "test", + "sip.to.tag": "FBcN7Xt0a8S1j", + "sip.to.uri.host": "10.0.2.15", + "sip.to.uri.original": "sip:test@10.0.2.15:5060", + "sip.to.uri.port": 5060, + "sip.to.uri.scheme": "sip", + "sip.to.uri.username": "test", + "sip.type": "request", + "sip.uri.host": "10.0.2.15", + "sip.uri.original": "sip:test@10.0.2.15:5060", + "sip.uri.port": 5060, + "sip.uri.scheme": "sip", + "sip.uri.username": "test", + "sip.version": "2.0", + "sip.via.original": [ + "SIP/2.0/UDP 10.0.2.20:5060;branch=z9hG4bK-2189-1-5" + ], + "source.ip": "10.0.2.20", + "source.port": 5060, + "status": "OK", + "type": "sip" + }, + { + "@metadata.beat": "packetbeat", + "@metadata.type": "_doc", + "client.ip": "10.0.2.15", + "client.port": 5060, + "destination.ip": "10.0.2.20", + "destination.port": 5060, + "event.action": "sip-bye", + "event.category": [ + "network", + "protocol" + ], + "event.dataset": "sip", + "event.duration": 0, + "event.kind": "event", + "event.original": "BYE sip:sipp@10.0.2.20:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 10.0.2.15;rport;branch=z9hG4bKe00pN1veeeyHp\r\nMax-Forwards: 70\r\nFrom: test ;tag=FBcN7Xt0a8S1j\r\nTo: \"DVI4/16000\" ;tag=1\r\nCall-ID: 1-2189@10.0.2.20\r\nCSeq: 99750437 BYE\r\nUser-Agent: FreeSWITCH-mod_sofia/1.6.12-20-b91a0a6~64bit\r\nAllow: INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, INFO, UPDATE, REGISTER, REFER, NOTIFY, PUBLISH, SUBSCRIBE\r\nSupported: timer, path, replaces\r\nReason: Q.850;cause=16;text=\"NORMAL_CLEARING\"\r\nContent-Length: 0\r\n\r\n", + "event.sequence": 99750437, + "event.type": [ + "info" + ], + "network.application": "sip", + "network.community_id": "1:xDRQZvk3ErEhBDslXv1c6EKI804=", + "network.iana_number": "17", + "network.protocol": "sip", + "network.transport": "udp", + "network.type": "ipv4", + "related.hosts": [ + "10.0.2.20", + "10.0.2.15" + ], + "related.ip": [ + "10.0.2.15", + "10.0.2.20" + ], + "related.user": [ + "sipp", + "test" + ], + "server.ip": "10.0.2.20", + "server.port": 5060, + "sip.allow": [ + "invite", + "ack", + "bye", + "cancel", + "options", + "message", + "info", + "update", + "register", + "refer", + "notify", + "publish", + "subscribe" + ], + "sip.call_id": "1-2189@10.0.2.20", + "sip.cseq.code": 99750437, + "sip.cseq.method": "BYE", + "sip.from.display_info": "test", + "sip.from.tag": "FBcN7Xt0a8S1j", + "sip.from.uri.host": "10.0.2.15", + "sip.from.uri.original": "sip:test@10.0.2.15:5060", + "sip.from.uri.port": 5060, + "sip.from.uri.scheme": "sip", + "sip.from.uri.username": "test", + "sip.max_forwards": 70, + "sip.method": "BYE", + "sip.supported": [ + "timer", + "path", + "replaces" + ], + "sip.to.display_info": "DVI4/16000", + "sip.to.tag": "1", + "sip.to.uri.host": "10.0.2.20", + "sip.to.uri.original": "sip:sipp@10.0.2.20:5060", + "sip.to.uri.port": 5060, + "sip.to.uri.scheme": "sip", + "sip.to.uri.username": "sipp", + "sip.type": "request", + "sip.uri.host": "10.0.2.20", + "sip.uri.original": "sip:sipp@10.0.2.20:5060", + "sip.uri.port": 5060, + "sip.uri.scheme": "sip", + "sip.uri.username": "sipp", + "sip.user_agent.original": "FreeSWITCH-mod_sofia/1.6.12-20-b91a0a6~64bit", + "sip.version": "2.0", + "sip.via.original": [ + "SIP/2.0/UDP 10.0.2.15;rport;branch=z9hG4bKe00pN1veeeyHp" + ], + "source.ip": "10.0.2.15", + "source.port": 5060, + "status": "OK", + "type": "sip" + } +] \ No newline at end of file diff --git a/packetbeat/tests/system/golden/sip_authenticated_register-expected.json b/packetbeat/tests/system/golden/sip_authenticated_register-expected.json new file mode 100644 index 00000000000..133792cc157 --- /dev/null +++ b/packetbeat/tests/system/golden/sip_authenticated_register-expected.json @@ -0,0 +1,142 @@ +[ + { + "@metadata.beat": "packetbeat", + "@metadata.type": "_doc", + "client.ip": "192.168.1.2", + "client.port": 5060, + "destination.ip": "212.242.33.35", + "destination.port": 5060, + "event.action": "sip-register", + "event.category": [ + "network", + "protocol", + "authentication" + ], + "event.dataset": "sip", + "event.duration": 0, + "event.kind": "event", + "event.original": "REGISTER sip:sip.cybercity.dk SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp112903503-43a64480192.168.1.2;rport\r\nFrom: ;tag=6bac55c\r\nTo: \r\nCall-ID: 578222729-4665d775@578222732-4665d772\r\nContact: ;expires=1200;q=0.500\r\nExpires: 1200\r\nCSeq: 75 REGISTER\r\nContent-Length: 0\r\nAuthorization: Digest username=\"voi18062\",realm=\"sip.cybercity.dk\",uri=\"sip:192.168.1.2\",nonce=\"1701b22972b90f440c3e4eb250842bb\",opaque=\"1701a1351f70795\",nc=\"00000001\",response=\"79a0543188495d288c9ebbe0c881abdc\"\r\nMax-Forwards: 70\r\nUser-Agent: Nero SIPPS IP Phone Version 2.0.51.16\r\n\r\n", + "event.sequence": 75, + "event.type": [ + "info" + ], + "network.application": "sip", + "network.community_id": "1:dOa61R2NaaJsJlcFAiMIiyXX+Kk=", + "network.iana_number": "17", + "network.protocol": "sip", + "network.transport": "udp", + "network.type": "ipv4", + "related.hosts": [ + "sip.cybercity.dk" + ], + "related.ip": [ + "192.168.1.2", + "212.242.33.35" + ], + "related.user": [ + "voi18062" + ], + "server.ip": "212.242.33.35", + "server.port": 5060, + "sip.auth.realm": "sip.cybercity.dk", + "sip.auth.scheme": "Digest", + "sip.auth.uri.host": "192.168.1.2", + "sip.auth.uri.original": "sip:192.168.1.2", + "sip.auth.uri.scheme": "sip", + "sip.call_id": "578222729-4665d775@578222732-4665d772", + "sip.contact.uri.host": "sip.cybercity.dk", + "sip.contact.uri.original": "sip:voi18062@sip.cybercity.dk", + "sip.contact.uri.scheme": "sip", + "sip.contact.uri.username": "voi18062", + "sip.cseq.code": 75, + "sip.cseq.method": "REGISTER", + "sip.from.tag": "6bac55c", + "sip.from.uri.host": "sip.cybercity.dk", + "sip.from.uri.original": "sip:voi18062@sip.cybercity.dk", + "sip.from.uri.scheme": "sip", + "sip.from.uri.username": "voi18062", + "sip.max_forwards": 70, + "sip.method": "REGISTER", + "sip.to.uri.host": "sip.cybercity.dk", + "sip.to.uri.original": "sip:voi18062@sip.cybercity.dk", + "sip.to.uri.scheme": "sip", + "sip.to.uri.username": "voi18062", + "sip.type": "request", + "sip.uri.host": "sip.cybercity.dk", + "sip.uri.original": "sip:sip.cybercity.dk", + "sip.uri.scheme": "sip", + "sip.user_agent.original": "Nero SIPPS IP Phone Version 2.0.51.16", + "sip.version": "2.0", + "sip.via.original": [ + "SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp112903503-43a64480192.168.1.2;rport" + ], + "source.ip": "192.168.1.2", + "source.port": 5060, + "status": "OK", + "type": "sip", + "user.name": "voi18062" + }, + { + "@metadata.beat": "packetbeat", + "@metadata.type": "_doc", + "client.ip": "212.242.33.35", + "client.port": 5060, + "destination.ip": "192.168.1.2", + "destination.port": 5060, + "event.action": "sip-register", + "event.category": [ + "network", + "protocol" + ], + "event.dataset": "sip", + "event.duration": 0, + "event.kind": "event", + "event.original": "SIP/2.0 100 Trying\r\nCall-ID: 578222729-4665d775@578222732-4665d772\r\nCSeq: 75 REGISTER\r\nFrom: ;tag=6bac55c\r\nTo: \r\nVia: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp112903503-43a64480192.168.1.2\r\nContent-Length: 0\r\n\r\n", + "event.reason": "Trying", + "event.sequence": 75, + "event.type": [ + "info" + ], + "network.application": "sip", + "network.community_id": "1:dOa61R2NaaJsJlcFAiMIiyXX+Kk=", + "network.iana_number": "17", + "network.protocol": "sip", + "network.transport": "udp", + "network.type": "ipv4", + "related.hosts": [ + "sip.cybercity.dk" + ], + "related.ip": [ + "212.242.33.35", + "192.168.1.2" + ], + "related.user": [ + "voi18062" + ], + "server.ip": "192.168.1.2", + "server.port": 5060, + "sip.call_id": "578222729-4665d775@578222732-4665d772", + "sip.code": 100, + "sip.cseq.code": 75, + "sip.cseq.method": "REGISTER", + "sip.from.tag": "6bac55c", + "sip.from.uri.host": "sip.cybercity.dk", + "sip.from.uri.original": "sip:voi18062@sip.cybercity.dk", + "sip.from.uri.scheme": "sip", + "sip.from.uri.username": "voi18062", + "sip.status": "Trying", + "sip.to.uri.host": "sip.cybercity.dk", + "sip.to.uri.original": "sip:voi18062@sip.cybercity.dk", + "sip.to.uri.scheme": "sip", + "sip.to.uri.username": "voi18062", + "sip.type": "response", + "sip.version": "2.0", + "sip.via.original": [ + "SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp112903503-43a64480192.168.1.2" + ], + "source.ip": "212.242.33.35", + "source.port": 5060, + "status": "OK", + "type": "sip" + } +] \ No newline at end of file diff --git a/packetbeat/tests/system/pcaps/sip.pcap b/packetbeat/tests/system/pcaps/sip.pcap new file mode 100644 index 0000000000000000000000000000000000000000..7ec19fb525be299140dbfde2d0eddb177a31cf42 GIT binary patch literal 6632 zcmeHL&2QUe71*j2&9-@KRu0?9_Ji>ENmPY^`=l8mC=14%1ubP3z&rp6ztY z4jkaXof}6!_6INt5C|?~H#QDv8aE_%KwRK7@g*d9UMFeXq}58>OhwwGKB^tRul@S> zJkRfaetz)v?e_!BIMcUrW(-a~zm;5DyLOU!3@6n6Ho@eXv7hdx=a{)u)enPA;05sK zPTxI!ckH{V6H0MaspgTRTars0cTV8Ld{_(%Q7OvDc%&-j88OUiZv~fBat(e*omYc%v*}{ZmAseS<+-8Y z1$=`mnD%vS*BlAOVbydFX}VmsZ4n7!%QAEgySmw&acWlo@)BuoxSJ9}NDya3!BB8Z zhRLSPp@>NTlp<2fkv>+Tpd)jkpe7GraAh9B+=h%>HQhupfC^Qr<(c$q8AU@uEZeTt zz?Kw6+faJH5^c+D8tqV!=D;720}Bv;>VETbK*A3h$bo;ppz)`9;N`%%$;;g!!!mhP zwcC1g<9{9R6^GbddIQ2Ip@L0_dQqun7r2I5yW*I29nT2iSXdA_k>}Qv0_Jh-%~)hz zcZb6Y@#x?>`_@UO1Z79vZxq+@H2$9A`uA-=uH)QU+{0A_TuY0`4O6e$h|?6z#e5ju%QH60t~28#^6NjQh-H(|U+qoOzK zCW{;cvq*1hMyp0II}T71S0j#Q>lUS_{>{VJJ2M(E2yejPR-=LKHeD?&q@U|gV7QzP zA#`+lZy?al1N)PE{3nXBP_RN8rc1aow%xWz&C@h1x_rMgLC&WvI!dN*26U6MY<&xB zZP~2XQ}8Z?H07r0y7qic_EszDSZsIO1b6u+F~q| zS%}8lc(+!h69`&Ad-p@T;Yz!x|I~Ipk^Ga%c$AOK&Qgj!6rCS4)~*ojPg>xQ{_zb~u%Dd?J4Ec6!h9`sC9IW*H<7b-z(y$FuccDX_o4 z=LdFt=B(BO_TVySq@a3xVSN4&iyL zC~Ofz+6&g9l+FN~_m$3mAm?Zn{6!wrh0|~Tmsq?hc^+Rf+j93q$PQoqc}ARSQk4s8FINEep%pxhAHmo!U-mJ25fv z1(?|w5epL%5?R;~>cRqi0hWG&a?XpCwx|UJSbBzg^qil&|NZ{Y;rrJwir^9$f5*oF z6vU!G_2KrL=TqQQ5ex#L2GBRax9^4M?Nv|##r~sZ0G>==li$586c3NC-d_tCMb=WF=`SiMJhiD{s3ShuA^}nLpis&dpfM zGkg|M8kD3)eBp+Ybcc01N_EkJ)U*g8Cb1RWFlf^>>HH|Ak#UNM`Z&s1kU8+wStgF` zP-Gyt6Gq%)0W*Bt44Ae_ImK+w<$K*Q;i-oSLhg=-REmUNm0{L_d0I57^Bo786prN{ z_T^Eg+~rX#YdcU>E%dT>oP-A~3**Rvi(!kW8SJGziP$#xqf``{HbYd`ux05sZ4%20Z0`FU1(t=G-wfoEbdT*ROL6j;CCyYwAg;MC zE~G59#N{2h%9A+fs#b^98mzVBh{Fw@q(T5Fz*8v}l$E5CloM`#ybK-+-18W+J>)zm zys!$uJTRx9ea-34DbEQnIIc8e7-EEABiRq5)*nD}_Wk}9zvntF;Q Date: Tue, 6 Oct 2020 13:18:49 +0200 Subject: [PATCH 32/93] Release cloudfoundry input and processor as GA (#21525) --- CHANGELOG.next.asciidoc | 2 ++ x-pack/filebeat/input/cloudfoundry/input.go | 2 +- .../docs/add_cloudfoundry_metadata.asciidoc | 2 -- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 15c2f9fe8a8..cd1e6c50a4c 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -450,6 +450,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Add Cloud Foundry tags in related events. {pull}21177[21177] - Cloud Foundry metadata is cached to disk. {pull}20775[20775] - Add option to select the type of index template to load: legacy, component, index. {pull}21212[21212] +- Release `add_cloudfoundry_metadata` as GA. {pull}21525[21525] *Auditbeat* @@ -606,6 +607,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Add related.hosts ecs field to all modules {pull}21160[21160] - Keep cursor state between httpjson input restarts {pull}20751[20751] - Convert aws s3 to v2 input {pull}20005[20005] +- Release Cloud Foundry input as GA. {pull}21525[21525] - New Cisco Umbrella dataset {pull}21504[21504] - New juniper.srx dataset for Juniper SRX logs. {pull}20017[20017] - Adding support for Microsoft 365 Defender (Microsoft Threat Protection) {pull}21446[21446] diff --git a/x-pack/filebeat/input/cloudfoundry/input.go b/x-pack/filebeat/input/cloudfoundry/input.go index 036a61b9d1e..3d2b9b34e59 100644 --- a/x-pack/filebeat/input/cloudfoundry/input.go +++ b/x-pack/filebeat/input/cloudfoundry/input.go @@ -25,7 +25,7 @@ type cloudfoundryEvent interface { func Plugin() v2.Plugin { return v2.Plugin{ Name: "cloudfoundry", - Stability: feature.Beta, + Stability: feature.Stable, Deprecated: false, Info: "collect logs from cloudfoundry loggregator", Manager: stateless.NewInputManager(configure), diff --git a/x-pack/libbeat/processors/add_cloudfoundry_metadata/docs/add_cloudfoundry_metadata.asciidoc b/x-pack/libbeat/processors/add_cloudfoundry_metadata/docs/add_cloudfoundry_metadata.asciidoc index 558b5a1031b..67e89c8173b 100644 --- a/x-pack/libbeat/processors/add_cloudfoundry_metadata/docs/add_cloudfoundry_metadata.asciidoc +++ b/x-pack/libbeat/processors/add_cloudfoundry_metadata/docs/add_cloudfoundry_metadata.asciidoc @@ -6,8 +6,6 @@ add_cloudfoundry_metadata ++++ -beta[] - The `add_cloudfoundry_metadata` processor annotates each event with relevant metadata from Cloud Foundry applications. The events are annotated with Cloud Foundry metadata, only if the event contains a reference to a Cloud Foundry application (using field From f5d13aa2037bb26b8b1f0dcc801b48abb299e8e8 Mon Sep 17 00:00:00 2001 From: Blake Rouse Date: Tue, 6 Oct 2020 09:02:26 -0400 Subject: [PATCH 33/93] [Elastic Agent] Add elastic agent ID and version to events from filebeat and metricbeat. (#21543) * Add elastic agent ID and version to events from filebeat and metricbeat. * Add changelog and fix inputs. --- x-pack/elastic-agent/CHANGELOG.next.asciidoc | 1 + .../pkg/agent/application/emitter.go | 11 +- .../pkg/agent/application/info/agent_info.go | 12 ++ .../agent/application/inspect_output_cmd.go | 45 ++++--- .../pkg/agent/application/local_mode.go | 1 + .../pkg/agent/application/managed_mode.go | 1 + .../agent/application/managed_mode_test.go | 4 +- .../agent/application/monitoring_decorator.go | 6 +- .../application/monitoring_decorator_test.go | 25 +++- .../pkg/agent/program/program.go | 8 +- .../pkg/agent/program/program_test.go | 16 ++- .../pkg/agent/program/supported.go | 2 +- .../testdata/enabled_output_true-filebeat.yml | 6 + .../testdata/enabled_true-filebeat.yml | 6 + .../testdata/single_config-filebeat.yml | 12 ++ .../testdata/single_config-metricbeat.yml | 19 ++- .../pkg/agent/transpiler/rules.go | 126 ++++++++++++++---- .../pkg/agent/transpiler/rules_test.go | 65 ++++++++- x-pack/elastic-agent/spec/filebeat.yml | 2 + x-pack/elastic-agent/spec/metricbeat.yml | 2 + 20 files changed, 301 insertions(+), 69 deletions(-) diff --git a/x-pack/elastic-agent/CHANGELOG.next.asciidoc b/x-pack/elastic-agent/CHANGELOG.next.asciidoc index 7d6870328c7..639b1dbad17 100644 --- a/x-pack/elastic-agent/CHANGELOG.next.asciidoc +++ b/x-pack/elastic-agent/CHANGELOG.next.asciidoc @@ -29,4 +29,5 @@ - Send `fleet.host.id` to Endpoint Security {pull}21042[21042] - Add `install` and `uninstall` subcommands {pull}21206[21206] - Send updating state {pull}21461[21461] +- Add `elastic.agent.id` and `elastic.agent.version` to published events from filebeat and metricbeat {pull}21543[21543] - Add `upgrade` subcommand to perform upgrade of installed Elastic Agent {pull}21425[21425] diff --git a/x-pack/elastic-agent/pkg/agent/application/emitter.go b/x-pack/elastic-agent/pkg/agent/application/emitter.go index d8a19492e2b..fc103366826 100644 --- a/x-pack/elastic-agent/pkg/agent/application/emitter.go +++ b/x-pack/elastic-agent/pkg/agent/application/emitter.go @@ -10,6 +10,7 @@ import ( "strings" "sync" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/info" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/program" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/transpiler" @@ -18,7 +19,7 @@ import ( "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger" ) -type decoratorFunc = func(string, *transpiler.AST, []program.Program) ([]program.Program, error) +type decoratorFunc = func(*info.AgentInfo, string, *transpiler.AST, []program.Program) ([]program.Program, error) type filterFunc = func(*logger.Logger, *transpiler.AST) error type reloadable interface { @@ -36,6 +37,7 @@ type programsDispatcher interface { type emitterController struct { logger *logger.Logger + agentInfo *info.AgentInfo controller composable.Controller router programsDispatcher modifiers *configModifiers @@ -112,14 +114,14 @@ func (e *emitterController) update() error { e.logger.Debug("Converting single configuration into specific programs configuration") - programsToRun, err := program.Programs(ast) + programsToRun, err := program.Programs(e.agentInfo, ast) if err != nil { return err } for _, decorator := range e.modifiers.Decorators { for outputType, ptr := range programsToRun { - programsToRun[outputType], err = decorator(outputType, ast, ptr) + programsToRun[outputType], err = decorator(e.agentInfo, outputType, ast, ptr) if err != nil { return err } @@ -135,12 +137,13 @@ func (e *emitterController) update() error { return e.router.Dispatch(ast.HashStr(), programsToRun) } -func emitter(ctx context.Context, log *logger.Logger, controller composable.Controller, router programsDispatcher, modifiers *configModifiers, reloadables ...reloadable) (emitterFunc, error) { +func emitter(ctx context.Context, log *logger.Logger, agentInfo *info.AgentInfo, controller composable.Controller, router programsDispatcher, modifiers *configModifiers, reloadables ...reloadable) (emitterFunc, error) { log.Debugf("Supported programs: %s", strings.Join(program.KnownProgramNames(), ", ")) init, _ := transpiler.NewVars(map[string]interface{}{}) ctrl := &emitterController{ logger: log, + agentInfo: agentInfo, controller: controller, router: router, modifiers: modifiers, 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 e990b83bd49..b0abbe19e64 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 @@ -4,6 +4,8 @@ package info +import "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/release" + // AgentInfo is a collection of information about agent. type AgentInfo struct { agentID string @@ -44,3 +46,13 @@ func ForceNewAgentInfo() (*AgentInfo, error) { func (i *AgentInfo) AgentID() string { return i.agentID } + +// Version returns the version for this Agent. +func (*AgentInfo) Version() string { + return release.Version() +} + +// Snapshot returns if this version is a snapshot. +func (*AgentInfo) Snapshot() bool { + return release.Snapshot() +} diff --git a/x-pack/elastic-agent/pkg/agent/application/inspect_output_cmd.go b/x-pack/elastic-agent/pkg/agent/application/inspect_output_cmd.go index 8f648887d10..bb319ce1569 100644 --- a/x-pack/elastic-agent/pkg/agent/application/inspect_output_cmd.go +++ b/x-pack/elastic-agent/pkg/agent/application/inspect_output_cmd.go @@ -8,11 +8,12 @@ import ( "context" "fmt" - "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/transpiler" - "github.com/elastic/beats/v7/libbeat/logp" + + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/info" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/configuration" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/program" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/transpiler" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/composable" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/config" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger" @@ -37,14 +38,19 @@ func NewInspectOutputCmd(configPath, output, program string) (*InspectOutputCmd, // Execute tries to enroll the agent into Fleet. func (c *InspectOutputCmd) Execute() error { + agentInfo, err := info.NewAgentInfo() + if err != nil { + return err + } + if c.output == "" { - return c.inspectOutputs() + return c.inspectOutputs(agentInfo) } - return c.inspectOutput() + return c.inspectOutput(agentInfo) } -func (c *InspectOutputCmd) inspectOutputs() error { +func (c *InspectOutputCmd) inspectOutputs(agentInfo *info.AgentInfo) error { rawConfig, err := loadConfig(c.cfgPath) if err != nil { return err @@ -61,7 +67,7 @@ func (c *InspectOutputCmd) inspectOutputs() error { } if isStandalone(cfg.Fleet) { - return listOutputsFromConfig(l, rawConfig) + return listOutputsFromConfig(l, agentInfo, rawConfig) } fleetConfig, err := loadFleetConfig(rawConfig) @@ -71,11 +77,11 @@ func (c *InspectOutputCmd) inspectOutputs() error { return fmt.Errorf("no fleet config retrieved yet") } - return listOutputsFromMap(l, fleetConfig) + return listOutputsFromMap(l, agentInfo, fleetConfig) } -func listOutputsFromConfig(log *logger.Logger, cfg *config.Config) error { - programsGroup, err := getProgramsFromConfig(log, cfg) +func listOutputsFromConfig(log *logger.Logger, agentInfo *info.AgentInfo, cfg *config.Config) error { + programsGroup, err := getProgramsFromConfig(log, agentInfo, cfg) if err != nil { return err @@ -88,16 +94,16 @@ func listOutputsFromConfig(log *logger.Logger, cfg *config.Config) error { return nil } -func listOutputsFromMap(log *logger.Logger, cfg map[string]interface{}) error { +func listOutputsFromMap(log *logger.Logger, agentInfo *info.AgentInfo, cfg map[string]interface{}) error { c, err := config.NewConfigFrom(cfg) if err != nil { return err } - return listOutputsFromConfig(log, c) + return listOutputsFromConfig(log, agentInfo, c) } -func (c *InspectOutputCmd) inspectOutput() error { +func (c *InspectOutputCmd) inspectOutput(agentInfo *info.AgentInfo) error { rawConfig, err := loadConfig(c.cfgPath) if err != nil { return err @@ -114,7 +120,7 @@ func (c *InspectOutputCmd) inspectOutput() error { } if isStandalone(cfg.Fleet) { - return printOutputFromConfig(l, c.output, c.program, rawConfig) + return printOutputFromConfig(l, agentInfo, c.output, c.program, rawConfig) } fleetConfig, err := loadFleetConfig(rawConfig) @@ -124,11 +130,11 @@ func (c *InspectOutputCmd) inspectOutput() error { return fmt.Errorf("no fleet config retrieved yet") } - return printOutputFromMap(l, c.output, c.program, fleetConfig) + return printOutputFromMap(l, agentInfo, c.output, c.program, fleetConfig) } -func printOutputFromConfig(log *logger.Logger, output, programName string, cfg *config.Config) error { - programsGroup, err := getProgramsFromConfig(log, cfg) +func printOutputFromConfig(log *logger.Logger, agentInfo *info.AgentInfo, output, programName string, cfg *config.Config) error { + programsGroup, err := getProgramsFromConfig(log, agentInfo, cfg) if err != nil { return err @@ -164,16 +170,16 @@ func printOutputFromConfig(log *logger.Logger, output, programName string, cfg * } -func printOutputFromMap(log *logger.Logger, output, programName string, cfg map[string]interface{}) error { +func printOutputFromMap(log *logger.Logger, agentInfo *info.AgentInfo, output, programName string, cfg map[string]interface{}) error { c, err := config.NewConfigFrom(cfg) if err != nil { return err } - return printOutputFromConfig(log, output, programName, c) + return printOutputFromConfig(log, agentInfo, output, programName, c) } -func getProgramsFromConfig(log *logger.Logger, cfg *config.Config) (map[string][]program.Program, error) { +func getProgramsFromConfig(log *logger.Logger, agentInfo *info.AgentInfo, cfg *config.Config) (map[string][]program.Program, error) { monitor := noop.NewMonitor() router := &inmemRouter{} ctx, cancel := context.WithCancel(context.Background()) @@ -186,6 +192,7 @@ func getProgramsFromConfig(log *logger.Logger, cfg *config.Config) (map[string][ emit, err := emitter( ctx, log, + agentInfo, composableWaiter, router, &configModifiers{ diff --git a/x-pack/elastic-agent/pkg/agent/application/local_mode.go b/x-pack/elastic-agent/pkg/agent/application/local_mode.go index f8eed0f5792..b58e260cab6 100644 --- a/x-pack/elastic-agent/pkg/agent/application/local_mode.go +++ b/x-pack/elastic-agent/pkg/agent/application/local_mode.go @@ -115,6 +115,7 @@ func newLocal( emit, err := emitter( localApplication.bgContext, log, + agentInfo, composableCtrl, router, &configModifiers{ diff --git a/x-pack/elastic-agent/pkg/agent/application/managed_mode.go b/x-pack/elastic-agent/pkg/agent/application/managed_mode.go index d1eaf197a88..647eae6d4e6 100644 --- a/x-pack/elastic-agent/pkg/agent/application/managed_mode.go +++ b/x-pack/elastic-agent/pkg/agent/application/managed_mode.go @@ -168,6 +168,7 @@ func newManaged( emit, err := emitter( managedApplication.bgContext, log, + agentInfo, composableCtrl, router, &configModifiers{ diff --git a/x-pack/elastic-agent/pkg/agent/application/managed_mode_test.go b/x-pack/elastic-agent/pkg/agent/application/managed_mode_test.go index 81f2419f936..65cb27547ff 100644 --- a/x-pack/elastic-agent/pkg/agent/application/managed_mode_test.go +++ b/x-pack/elastic-agent/pkg/agent/application/managed_mode_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/info" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/configrequest" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/composable" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger" @@ -32,8 +33,9 @@ func TestManagedModeRouting(t *testing.T) { log, _ := logger.New("") router, _ := newRouter(log, streamFn) + agentInfo, _ := info.NewAgentInfo() composableCtrl, _ := composable.New(log, nil) - emit, err := emitter(ctx, log, composableCtrl, router, &configModifiers{Decorators: []decoratorFunc{injectMonitoring}}) + emit, err := emitter(ctx, log, agentInfo, composableCtrl, router, &configModifiers{Decorators: []decoratorFunc{injectMonitoring}}) require.NoError(t, err) actionDispatcher, err := newActionDispatcher(ctx, log, &handlerDefault{log: log}) diff --git a/x-pack/elastic-agent/pkg/agent/application/monitoring_decorator.go b/x-pack/elastic-agent/pkg/agent/application/monitoring_decorator.go index 2b04126381b..3fc49ef17d3 100644 --- a/x-pack/elastic-agent/pkg/agent/application/monitoring_decorator.go +++ b/x-pack/elastic-agent/pkg/agent/application/monitoring_decorator.go @@ -7,6 +7,7 @@ package application import ( "fmt" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/info" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/program" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/transpiler" ) @@ -28,7 +29,7 @@ const ( defaultOutputName = "default" ) -func injectMonitoring(outputGroup string, rootAst *transpiler.AST, programsToRun []program.Program) ([]program.Program, error) { +func injectMonitoring(agentInfo *info.AgentInfo, outputGroup string, rootAst *transpiler.AST, programsToRun []program.Program) ([]program.Program, error) { var err error monitoringProgram := program.Program{ Spec: program.Spec{ @@ -63,7 +64,7 @@ func injectMonitoring(outputGroup string, rootAst *transpiler.AST, programsToRun } ast := rootAst.Clone() - if err := getMonitoringRule(monitoringOutputName).Apply(ast); err != nil { + if err := getMonitoringRule(monitoringOutputName).Apply(agentInfo, ast); err != nil { return programsToRun, err } @@ -93,6 +94,7 @@ func getMonitoringRule(outputName string) *transpiler.RuleList { return transpiler.NewRuleList( transpiler.Copy(monitoringOutputSelector, outputKey), transpiler.Rename(fmt.Sprintf("%s.%s", outputsKey, outputName), elasticsearchKey), + transpiler.InjectAgentInfo(), transpiler.Filter(monitoringKey, programsKey, outputKey), ) } diff --git a/x-pack/elastic-agent/pkg/agent/application/monitoring_decorator_test.go b/x-pack/elastic-agent/pkg/agent/application/monitoring_decorator_test.go index f50bb74d5e8..6a3be4100be 100644 --- a/x-pack/elastic-agent/pkg/agent/application/monitoring_decorator_test.go +++ b/x-pack/elastic-agent/pkg/agent/application/monitoring_decorator_test.go @@ -7,17 +7,22 @@ package application import ( "testing" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/info" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/program" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/transpiler" ) func TestMonitoringInjection(t *testing.T) { + agentInfo, err := info.NewAgentInfo() + if err != nil { + t.Fatal(err) + } ast, err := transpiler.NewAST(inputConfigMap) if err != nil { t.Fatal(err) } - programsToRun, err := program.Programs(ast) + programsToRun, err := program.Programs(agentInfo, ast) if err != nil { t.Fatal(err) } @@ -25,7 +30,7 @@ func TestMonitoringInjection(t *testing.T) { GROUPLOOP: for group, ptr := range programsToRun { programsCount := len(ptr) - newPtr, err := injectMonitoring(group, ast, ptr) + newPtr, err := injectMonitoring(agentInfo, group, ast, ptr) if err != nil { t.Error(err) continue GROUPLOOP @@ -83,12 +88,16 @@ GROUPLOOP: } func TestMonitoringInjectionDefaults(t *testing.T) { + agentInfo, err := info.NewAgentInfo() + if err != nil { + t.Fatal(err) + } ast, err := transpiler.NewAST(inputConfigMapDefaults) if err != nil { t.Fatal(err) } - programsToRun, err := program.Programs(ast) + programsToRun, err := program.Programs(agentInfo, ast) if err != nil { t.Fatal(err) } @@ -96,7 +105,7 @@ func TestMonitoringInjectionDefaults(t *testing.T) { GROUPLOOP: for group, ptr := range programsToRun { programsCount := len(ptr) - newPtr, err := injectMonitoring(group, ast, ptr) + newPtr, err := injectMonitoring(agentInfo, group, ast, ptr) if err != nil { t.Error(err) continue GROUPLOOP @@ -154,12 +163,16 @@ GROUPLOOP: } func TestMonitoringInjectionDisabled(t *testing.T) { + agentInfo, err := info.NewAgentInfo() + if err != nil { + t.Fatal(err) + } ast, err := transpiler.NewAST(inputConfigMapDisabled) if err != nil { t.Fatal(err) } - programsToRun, err := program.Programs(ast) + programsToRun, err := program.Programs(agentInfo, ast) if err != nil { t.Fatal(err) } @@ -167,7 +180,7 @@ func TestMonitoringInjectionDisabled(t *testing.T) { GROUPLOOP: for group, ptr := range programsToRun { programsCount := len(ptr) - newPtr, err := injectMonitoring(group, ast, ptr) + newPtr, err := injectMonitoring(agentInfo, group, ast, ptr) if err != nil { t.Error(err) continue GROUPLOOP diff --git a/x-pack/elastic-agent/pkg/agent/program/program.go b/x-pack/elastic-agent/pkg/agent/program/program.go index 25b56081e68..f3f17d06b9d 100644 --- a/x-pack/elastic-agent/pkg/agent/program/program.go +++ b/x-pack/elastic-agent/pkg/agent/program/program.go @@ -47,7 +47,7 @@ func (p *Program) Configuration() map[string]interface{} { // Programs take a Tree representation of the main configuration and apply all the different // programs rules and generate individual configuration from the rules. -func Programs(singleConfig *transpiler.AST) (map[string][]Program, error) { +func Programs(agentInfo transpiler.AgentInfo, singleConfig *transpiler.AST) (map[string][]Program, error) { grouped, err := groupByOutputs(singleConfig) if err != nil { return nil, errors.New(err, errors.TypeConfig, "fail to extract program configuration") @@ -55,7 +55,7 @@ func Programs(singleConfig *transpiler.AST) (map[string][]Program, error) { groupedPrograms := make(map[string][]Program) for k, config := range grouped { - programs, err := detectPrograms(config) + programs, err := detectPrograms(agentInfo, config) if err != nil { return nil, errors.New(err, errors.TypeConfig, "fail to generate program configuration") } @@ -65,11 +65,11 @@ func Programs(singleConfig *transpiler.AST) (map[string][]Program, error) { return groupedPrograms, nil } -func detectPrograms(singleConfig *transpiler.AST) ([]Program, error) { +func detectPrograms(agentInfo transpiler.AgentInfo, singleConfig *transpiler.AST) ([]Program, error) { programs := make([]Program, 0) for _, spec := range Supported { specificAST := singleConfig.Clone() - err := spec.Rules.Apply(specificAST) + err := spec.Rules.Apply(agentInfo, specificAST) if err != nil { return nil, err } 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 c15510b6655..8c2cf8c499f 100644 --- a/x-pack/elastic-agent/pkg/agent/program/program_test.go +++ b/x-pack/elastic-agent/pkg/agent/program/program_test.go @@ -437,7 +437,7 @@ func TestConfiguration(t *testing.T) { ast, err := transpiler.NewAST(m) require.NoError(t, err) - programs, err := Programs(ast) + programs, err := Programs(&fakeAgentInfo{}, ast) if test.err { require.Error(t, err) return @@ -478,3 +478,17 @@ func TestConfiguration(t *testing.T) { }) } } + +type fakeAgentInfo struct{} + +func (*fakeAgentInfo) AgentID() string { + return "agent-id" +} + +func (*fakeAgentInfo) Version() string { + return "8.0.0" +} + +func (*fakeAgentInfo) Snapshot() bool { + return false +} diff --git a/x-pack/elastic-agent/pkg/agent/program/supported.go b/x-pack/elastic-agent/pkg/agent/program/supported.go index 3b314bfa3f4..adc13938ae2 100644 --- a/x-pack/elastic-agent/pkg/agent/program/supported.go +++ b/x-pack/elastic-agent/pkg/agent/program/supported.go @@ -21,7 +21,7 @@ func init() { // spec/filebeat.yml // spec/heartbeat.yml // spec/metricbeat.yml - unpacked := packer.MustUnpack("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") + unpacked := packer.MustUnpack("eJy8WF1zozoSfd+fMa93axdEnF226j4YUnzFJmOSSEJvSLIBW2DfALbx1v73LQHmw0lm7uxszcOUByKk7lb36XP631+Kw5r9fZ3zwz7Ny7/Vmfjyry80s0ryso8DNNsxWz/QfBW/Arjl2DtwZ/cYAnX3lBqCZsGJAlFxU70Q5KssE8p6dUhYHhxIZm35wz4mwx4lsSEwc1+wnBxC8HrvPoTa00P8GIJEhKDcRGh24bZV0If94+LZEGsbbjEgB2q/3pvpPHZN4xTiYP+UztPxvmywLe3WJSzjl6d4H7vmPF48z1OewTpCZOZ277gtSoJ0Vdq4vMwfma1fuCX385UQnYuneF+6NrwjyN+QTBTkZf8ov3MdI+F2fO+a3sf+P7vtOtuqibbs7J6Xrun1e7sjuxbPqspsXocoEDfva4L9I8feluBlOtrnk3Mn66t1Jk4f+epv5yczN2oCdZVmomJakFD7dG+mSkxwIkJVzyJ0FtfYMdtSood97GawIo5xjNBMWWBfhBqsIxz08Qyxl7NLF6NrzNHsnc/vbfFUasNLG29yWFv6hTueCJFy7zqlbnbvqRMIJnQQorNK8DWuxoWgswi14Mi2+zhCsxPHwaX72xvBu3vXCWbMfu3ujiTUgWKwUxnn52MTg0wU3IY11m7WOr6gNtxyW6+fUuNAc0PlzrK761KsX5pcT8LsLMi88zWzCo7gKA8NheVQND5d92tyLjj28QawIMhXqOZdnlKDErkfXlUh8rcE+xcMrFMEdelb4dqkIAgqi6w8hJlVhVCZ5mj/d+sUrZqaKkM8v6klI6M2FLyzmeWwGOI7L13HExTpgLRnXt83/yIAZ0+pkYTAF0zzNyE2DhiUYr3q/a0JUo88g5tmbefjOGYREGmIZsnknrfvYz65szYm/fP03uela+sqdwz16lNjByYHBsSRxvtHDhJBt/uY2rAiWrB/NIN/tHsG+uPz/Df3YR6HaLZz7bOgGVciM96tgaiYAxWmKQf34S5emkZCs1Uc2dblGcCZ3INqUJFrNs+n2AOwCLGvRMi/EGTVIYjzx9X+9y9/bSF3k4o1XUfvIFdCDfJEiFdXmG3KMcxgwueHFtZSg7qparnpKXZzX3AHnhaZKOjzTNDMSqkNd1+RTF9fNGtu1+aBoNgoQhyIRQarEHkFQSudZFbBwGu6MOfp4rX9pciqQsQFRbDi5qykIBBfcVwy29pGtdqmjukWrumWwbP89Up5nQTAkkioGO3PHU8lz5O1BQU8j9AsX2RnwTNYfEWBCHOYu0J5DLGnRIgkoba6d20Zk+CyaNoBTAmylO9CR9qkxh+ynDAQFbHh3TUFuSNOMt7U1nN2akrjQLODhJIN04KaIKvEmlHTNrWPfUraeoWBf6QZKSLkKy0UyJYWbEJEFII7+G9h5961z0eiLRtoocg63cLqDWTVHJ0n8BQC/bSGekLt84bb+oba4sIfBph1TUOhl318tZmdxiX2ztaKAv00LmGCky3BhtLkVO4rLIMJxcvm7iO0an57WGvu2TuxTG+gSEKUvKcbWxWq6kWEfWVa7kIhzb2MYpov/1c/hphnMKOa10GqbI1NHXV3RWoKlHvX7kr3dG1B/xzeab3Pj10LVJikLlbrAwbSbvXy2b3d2hvhQNCX935Mzjx9CsXTtuL0+T20j8yqGDgnvKdE84ldTV6vxrFTE+YYA6T2789H0lGr5v/jeDd5QQTNV8eO+jR1Mj7PNQ1ZrxU39Qu3g4OEU6YFuwjd3ZwDQYMDWrBl0j7bP32yj0qc+b3rwB2bT22RZy9AcAxBKf2Iia1vIwDrm30KCtiRZXAXYX/DwPnIwflIZE4175bv/a/1yxr78rt71/Fn8ptrHP5M6+LYFxh80Gq+8x2xLSWEPVb19cMyWFKNiKaFvkxqvKUydpBw2+rxaZHNEorgRWIx+YGWe3N+1TxjX1IAmZey3ygEe5tb+jJQE/ddTXVUQFljQ3Q5fUPVZFu2NAb8gmpwx4GlhCAesAMfVJa9lm3eBXuO3BGunI8cBRnVJAX1ZsN+/pHmQRKhmWBDjewo8N96HJZ0AehHAs5igQ01zH01HNbuuROcMBio7bB3onDH+IMBvRrelQnJymR4nuRLyXAw+n4muE0KqrHeDnpZAh9ZKrGFMqZXxBYDdXGWo//7CrFFNXq+zVEl1Ob9/hwFp2EtrCI8xJcDUTU1/LN02u77+aeUuunzq2lPbThEHkiJl0mcX2tKg7dNz/hTeD3q+z9EFQc+1d9DR9NG9yrWuKHcwsz5nqC7+yn1G85eZD9PAxfmPO8wKV80uMHfQkTewmdWuCaXnEjK4EtksoMZ/95TxmQdvZUfcMZnG0rJ3cYm80si+9PkXStFXavoeR0DUOF4XkXoXH6PA17XchuWzG56T9Vzggc1C9H5csPrbjigeiT2q7421VOI/LcFamXJhJtmakIzKydIlX1nvH8jmaZrZe/iB5qxijb95aQTG6YcsRTfjB8a3uwsj5N45OO+2uTIHb7ygJc2N9u8uK0Tf8OBUCJLrwniYu3Mhx587QmT/mvU8n5w7s8kphEcHEJteVykRY/tn2PqN6Tod7D401r9QJLe1Ozk3GHNCBfy5YR3NDU+5izmxxKsl2oAzpjcJ3v9UIZdMWfzvIu/pvOTa1sVMY19iP0Fwbu955RHjgO5Rpd1RNA5YZqMqy9C7G0js6mhmqDgwGpWSFs90OK2V8t6lHzcl/1u79W7x2ttZevyLWUfFNcLggrLxLYTYFuKpBhRBXe8Qwg6odbOPmJU9wVwIThQmTk7UFv5XrFc10qieaK2pZDvCbmbYqFI35EX9W6BpRYvyk4ff0vIDfvjoOboRvTZek6kYKpnRdOQH9QdQZ5Kao9LMOG2yMKWiDcFxWq9JDioI+R3BWYcmRZM5nltUrTEZDJPm8yY1CNxmvlDRcyGnEnRUK2R2s+PpFCQ8SZ4dS+BhoKgKeZFtjoyTVwkSC1yUVJzJongVbg8DnONjwt+3KwiNNsRHF+bYkNqnlLj6uOlbUCiirJmftMRKXXDHO8YAnhhQO+Lh4LZJgR6RbLzoRWyomIA1tzSE5IHPXHpBWmXb51IqGXuUNTPPzOW6eV7oRAch3f+1Z7OTjVhDzezyw/EzyeCY0s1Y4aBVVDrE2HXnj2cOQKH977PjrQnLC35X9u+YM6qaU69aKqbujh0grPP1XaIMRGQKV7d2KoFRwzOB6atprOqqzAb3dFEZP6QH/0dpgSRBsx+sXh8R9Kxxg/cTjYsgznBST9o+ICYt00pvXtbgA7HtOXumwTw15LGnxTG8PMm/S2h7HiyxtePD/rqazu8+W2RFof3MeoaqTzjYR974/lyK+KqEKliKrw6YjtZOww9JH5zdBYDIVWTCMBNiL06vJ25djnS4wToiewoV642+2JozH9GYI6++xFBezP3/rUiuHm+jOfBv0pI3wwA/s+CZ8ox5Le3uUVy7zjOhYajSM7xvjf0ffJHxNNk3+FuOwI2mr+PSN3HAmriS3XN958SUa1w6oned0XUf/7y3wAAAP//NCXBlQ==") SupportedMap = make(map[string]Spec) for f, v := range unpacked { diff --git a/x-pack/elastic-agent/pkg/agent/program/testdata/enabled_output_true-filebeat.yml b/x-pack/elastic-agent/pkg/agent/program/testdata/enabled_output_true-filebeat.yml index 8edc27061b0..38b251d95dc 100644 --- a/x-pack/elastic-agent/pkg/agent/program/testdata/enabled_output_true-filebeat.yml +++ b/x-pack/elastic-agent/pkg/agent/program/testdata/enabled_output_true-filebeat.yml @@ -16,6 +16,12 @@ filebeat: target: "event" fields: dataset: generic + - add_fields: + target: "elastic" + fields: + agent.id: agent-id + agent.version: 8.0.0 + agent.snapshot: false output: elasticsearch: enabled: true diff --git a/x-pack/elastic-agent/pkg/agent/program/testdata/enabled_true-filebeat.yml b/x-pack/elastic-agent/pkg/agent/program/testdata/enabled_true-filebeat.yml index 8bd5d93a3b9..6e768db6aa4 100644 --- a/x-pack/elastic-agent/pkg/agent/program/testdata/enabled_true-filebeat.yml +++ b/x-pack/elastic-agent/pkg/agent/program/testdata/enabled_true-filebeat.yml @@ -17,6 +17,12 @@ filebeat: target: "event" fields: dataset: generic + - add_fields: + target: "elastic" + fields: + agent.id: agent-id + agent.version: 8.0.0 + agent.snapshot: false output: elasticsearch: hosts: diff --git a/x-pack/elastic-agent/pkg/agent/program/testdata/single_config-filebeat.yml b/x-pack/elastic-agent/pkg/agent/program/testdata/single_config-filebeat.yml index b996e13b531..01ee955e4ec 100644 --- a/x-pack/elastic-agent/pkg/agent/program/testdata/single_config-filebeat.yml +++ b/x-pack/elastic-agent/pkg/agent/program/testdata/single_config-filebeat.yml @@ -18,6 +18,12 @@ filebeat: target: "event" fields: dataset: generic + - add_fields: + target: "elastic" + fields: + agent.id: agent-id + agent.version: 8.0.0 + agent.snapshot: false - type: log paths: - /var/log/hello3.log @@ -36,6 +42,12 @@ filebeat: target: "event" fields: dataset: generic + - add_fields: + target: "elastic" + fields: + agent.id: agent-id + agent.version: 8.0.0 + agent.snapshot: false output: elasticsearch: hosts: diff --git a/x-pack/elastic-agent/pkg/agent/program/testdata/single_config-metricbeat.yml b/x-pack/elastic-agent/pkg/agent/program/testdata/single_config-metricbeat.yml index c62882ff6da..d09e80accf1 100644 --- a/x-pack/elastic-agent/pkg/agent/program/testdata/single_config-metricbeat.yml +++ b/x-pack/elastic-agent/pkg/agent/program/testdata/single_config-metricbeat.yml @@ -15,6 +15,12 @@ metricbeat: target: "event" fields: dataset: docker.status + - add_fields: + target: "elastic" + fields: + agent.id: agent-id + agent.version: 8.0.0 + agent.snapshot: false - module: docker metricsets: [info] index: metrics-generic-default @@ -30,6 +36,12 @@ metricbeat: target: "event" fields: dataset: generic + - add_fields: + target: "elastic" + fields: + agent.id: agent-id + agent.version: 8.0.0 + agent.snapshot: false - module: apache metricsets: [info] index: metrics-generic-testing @@ -48,7 +60,12 @@ metricbeat: target: "event" fields: dataset: generic - + - add_fields: + target: "elastic" + fields: + agent.id: agent-id + agent.version: 8.0.0 + agent.snapshot: false output: elasticsearch: hosts: [127.0.0.1:9200, 127.0.0.1:9300] diff --git a/x-pack/elastic-agent/pkg/agent/transpiler/rules.go b/x-pack/elastic-agent/pkg/agent/transpiler/rules.go index 5ad790eb31e..29ff1786d1e 100644 --- a/x-pack/elastic-agent/pkg/agent/transpiler/rules.go +++ b/x-pack/elastic-agent/pkg/agent/transpiler/rules.go @@ -14,6 +14,13 @@ import ( "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" ) +// AgentInfo is an interface to get the agent info. +type AgentInfo interface { + AgentID() string + Version() string + Snapshot() bool +} + // RuleList is a container that allow the same tree to be executed on multiple defined Rule. type RuleList struct { Rules []Rule @@ -21,15 +28,15 @@ type RuleList struct { // Rule defines a rule that can be Applied on the Tree. type Rule interface { - Apply(*AST) error + Apply(AgentInfo, *AST) error } // Apply applies a list of rules over the same tree and use the result of the previous execution // as the input of the next rule, will return early if any error is raise during the execution. -func (r *RuleList) Apply(ast *AST) error { +func (r *RuleList) Apply(agentInfo AgentInfo, ast *AST) error { var err error for _, rule := range r.Rules { - err = rule.Apply(ast) + err = rule.Apply(agentInfo, ast) if err != nil { return err } @@ -73,6 +80,8 @@ func (r *RuleList) MarshalYAML() (interface{}, error) { name = "inject_index" case *InjectStreamProcessorRule: name = "inject_stream_processor" + case *InjectAgentInfoRule: + name = "inject_agent_info" case *MakeArrayRule: name = "make_array" case *RemoveKeyRule: @@ -154,6 +163,8 @@ func (r *RuleList) UnmarshalYAML(unmarshal func(interface{}) error) error { r = &InjectIndexRule{} case "inject_stream_processor": r = &InjectStreamProcessorRule{} + case "inject_agent_info": + r = &InjectAgentInfoRule{} case "make_array": r = &MakeArrayRule{} case "remove_key": @@ -181,7 +192,7 @@ type SelectIntoRule struct { } // Apply applies select into rule. -func (r *SelectIntoRule) Apply(ast *AST) error { +func (r *SelectIntoRule) Apply(_ AgentInfo, ast *AST) error { target := &Dict{} for _, selector := range r.Selectors { @@ -214,7 +225,7 @@ type RemoveKeyRule struct { } // Apply applies remove key rule. -func (r *RemoveKeyRule) Apply(ast *AST) error { +func (r *RemoveKeyRule) Apply(_ AgentInfo, ast *AST) error { sourceMap, ok := ast.root.(*Dict) if !ok { return nil @@ -250,7 +261,7 @@ type MakeArrayRule struct { } // Apply applies make array rule. -func (r *MakeArrayRule) Apply(ast *AST) error { +func (r *MakeArrayRule) Apply(_ AgentInfo, ast *AST) error { sourceNode, found := Lookup(ast, r.Item) if !found { return nil @@ -286,7 +297,7 @@ type CopyToListRule struct { } // Apply copies specified node into every item of the list. -func (r *CopyToListRule) Apply(ast *AST) error { +func (r *CopyToListRule) Apply(_ AgentInfo, ast *AST) error { sourceNode, found := Lookup(ast, r.Item) if !found { // nothing to copy @@ -347,7 +358,7 @@ type CopyAllToListRule struct { } // Apply copies all nodes into every item of the list. -func (r *CopyAllToListRule) Apply(ast *AST) error { +func (r *CopyAllToListRule) Apply(agentInfo AgentInfo, ast *AST) error { // get list of nodes astMap, err := ast.Map() if err != nil { @@ -370,7 +381,7 @@ func (r *CopyAllToListRule) Apply(ast *AST) error { continue } - if err := CopyToList(item, r.To, r.OnConflict).Apply(ast); err != nil { + if err := CopyToList(item, r.To, r.OnConflict).Apply(agentInfo, ast); err != nil { return err } } @@ -393,7 +404,7 @@ type FixStreamRule struct { } // Apply stream fixes. -func (r *FixStreamRule) Apply(ast *AST) error { +func (r *FixStreamRule) Apply(_ AgentInfo, ast *AST) error { const defaultDataset = "generic" const defaultNamespace = "default" @@ -526,7 +537,7 @@ type InjectIndexRule struct { } // Apply injects index into input. -func (r *InjectIndexRule) Apply(ast *AST) error { +func (r *InjectIndexRule) Apply(_ AgentInfo, ast *AST) error { inputsNode, found := Lookup(ast, "inputs") if !found { return nil @@ -583,7 +594,7 @@ type InjectStreamProcessorRule struct { } // Apply injects processor into input. -func (r *InjectStreamProcessorRule) Apply(ast *AST) error { +func (r *InjectStreamProcessorRule) Apply(_ AgentInfo, ast *AST) error { inputsNode, found := Lookup(ast, "inputs") if !found { return nil @@ -665,6 +676,63 @@ func InjectStreamProcessor(onMerge, streamType string) *InjectStreamProcessorRul } } +// InjectAgentInfoRule injects agent information into each rule. +type InjectAgentInfoRule struct{} + +// Apply injects index into input. +func (r *InjectAgentInfoRule) Apply(agentInfo AgentInfo, ast *AST) error { + inputsNode, found := Lookup(ast, "inputs") + if !found { + return nil + } + + inputsList, ok := inputsNode.Value().(*List) + if !ok { + return nil + } + + for _, inputNode := range inputsList.value { + inputMap, ok := inputNode.(*Dict) + if !ok { + continue + } + + // get processors node + processorsNode, found := inputMap.Find("processors") + if !found { + processorsNode = &Key{ + name: "processors", + value: &List{value: make([]Node, 0)}, + } + + inputMap.value = append(inputMap.value, processorsNode) + } + + processorsList, ok := processorsNode.Value().(*List) + if !ok { + return errors.New("InjectAgentInfoRule: processors is not a list") + } + + // elastic.agent + processorMap := &Dict{value: make([]Node, 0)} + processorMap.value = append(processorMap.value, &Key{name: "target", value: &StrVal{value: "elastic"}}) + processorMap.value = append(processorMap.value, &Key{name: "fields", value: &Dict{value: []Node{ + &Key{name: "agent.id", value: &StrVal{value: agentInfo.AgentID()}}, + &Key{name: "agent.version", value: &StrVal{value: agentInfo.Version()}}, + &Key{name: "agent.snapshot", value: &BoolVal{value: agentInfo.Snapshot()}}, + }}}) + addFieldsMap := &Dict{value: []Node{&Key{"add_fields", processorMap}}} + processorsList.value = mergeStrategy("").InjectItem(processorsList.value, addFieldsMap) + } + + return nil +} + +// InjectAgentInfo creates a InjectAgentInfoRule +func InjectAgentInfo() *InjectAgentInfoRule { + return &InjectAgentInfoRule{} +} + // ExtractListItemRule extract items with specified name from a list of maps. // The result is store in a new array. // Example: @@ -679,7 +747,7 @@ type ExtractListItemRule struct { } // Apply extracts items from array. -func (r *ExtractListItemRule) Apply(ast *AST) error { +func (r *ExtractListItemRule) Apply(_ AgentInfo, ast *AST) error { node, found := Lookup(ast, r.Path) if !found { return nil @@ -740,7 +808,7 @@ type RenameRule struct { // Apply renames the last items of a Selector to a new name and keep all the other values and will // return an error on failure. -func (r *RenameRule) Apply(ast *AST) error { +func (r *RenameRule) Apply(_ AgentInfo, ast *AST) error { // Skip rename when node is not found. node, ok := Lookup(ast, r.From) if !ok { @@ -773,7 +841,7 @@ func Copy(from, to Selector) *CopyRule { } // Apply copy a part of a tree into a new destination. -func (r CopyRule) Apply(ast *AST) error { +func (r CopyRule) Apply(_ AgentInfo, ast *AST) error { node, ok := Lookup(ast, r.From) // skip when the `from` node is not found. if !ok { @@ -800,7 +868,7 @@ func Translate(path Selector, mapper map[string]interface{}) *TranslateRule { } // Apply translates matching elements of a translation table for a specific selector. -func (r *TranslateRule) Apply(ast *AST) error { +func (r *TranslateRule) Apply(_ AgentInfo, ast *AST) error { // Skip translate when node is not found. node, ok := Lookup(ast, r.Path) if !ok { @@ -873,7 +941,7 @@ func TranslateWithRegexp(path Selector, re *regexp.Regexp, with string) *Transla } // Apply translates matching elements of a translation table for a specific selector. -func (r *TranslateWithRegexpRule) Apply(ast *AST) error { +func (r *TranslateWithRegexpRule) Apply(_ AgentInfo, ast *AST) error { // Skip translate when node is not found. node, ok := Lookup(ast, r.Path) if !ok { @@ -914,7 +982,7 @@ func Map(path Selector, rules ...Rule) *MapRule { } // Apply maps multiples rules over a subset of the tree. -func (r *MapRule) Apply(ast *AST) error { +func (r *MapRule) Apply(agentInfo AgentInfo, ast *AST) error { node, ok := Lookup(ast, r.Path) // Skip map when node is not found. if !ok { @@ -931,15 +999,15 @@ func (r *MapRule) Apply(ast *AST) error { switch t := n.Value().(type) { case *List: - return mapList(r, t) + return mapList(agentInfo, r, t) case *Dict: - return mapDict(r, t) + return mapDict(agentInfo, r, t) case *Key: switch t := n.Value().(type) { case *List: - return mapList(r, t) + return mapList(agentInfo, r, t) case *Dict: - return mapDict(r, t) + return mapDict(agentInfo, r, t) default: return fmt.Errorf( "cannot iterate over node, invalid type expected 'List' or 'Dict' received '%T'", @@ -954,13 +1022,13 @@ func (r *MapRule) Apply(ast *AST) error { ) } -func mapList(r *MapRule, l *List) error { +func mapList(agentInfo AgentInfo, r *MapRule, l *List) error { values := l.Value().([]Node) for idx, item := range values { newAST := &AST{root: item} for _, rule := range r.Rules { - err := rule.Apply(newAST) + err := rule.Apply(agentInfo, newAST) if err != nil { return err } @@ -970,10 +1038,10 @@ func mapList(r *MapRule, l *List) error { return nil } -func mapDict(r *MapRule, l *Dict) error { +func mapDict(agentInfo AgentInfo, r *MapRule, l *Dict) error { newAST := &AST{root: l} for _, rule := range r.Rules { - err := rule.Apply(newAST) + err := rule.Apply(agentInfo, newAST) if err != nil { return err } @@ -1024,7 +1092,7 @@ func Filter(selectors ...Selector) *FilterRule { } // Apply filters a Tree based on list of selectors. -func (r *FilterRule) Apply(ast *AST) error { +func (r *FilterRule) Apply(_ AgentInfo, ast *AST) error { mergedAST := &AST{root: &Dict{}} var err error for _, selector := range r.Selectors { @@ -1054,7 +1122,7 @@ func FilterValues(selector Selector, key Selector, values ...interface{}) *Filte } // Apply filters a Tree based on list of selectors. -func (r *FilterValuesRule) Apply(ast *AST) error { +func (r *FilterValuesRule) Apply(_ AgentInfo, ast *AST) error { node, ok := Lookup(ast, r.Selector) // Skip map when node is not found. if !ok { @@ -1167,7 +1235,7 @@ func (r *FilterValuesWithRegexpRule) UnmarshalYAML(unmarshal func(interface{}) e } // Apply filters a Tree based on list of selectors. -func (r *FilterValuesWithRegexpRule) Apply(ast *AST) error { +func (r *FilterValuesWithRegexpRule) Apply(_ AgentInfo, ast *AST) error { node, ok := Lookup(ast, r.Selector) // Skip map when node is not found. if !ok { 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 c3207f48cea..d92ba0de985 100644 --- a/x-pack/elastic-agent/pkg/agent/transpiler/rules_test.go +++ b/x-pack/elastic-agent/pkg/agent/transpiler/rules_test.go @@ -165,6 +165,51 @@ inputs: }, }, + "inject agent info": { + givenYAML: ` +inputs: + - name: No processors + type: file + - name: With processors + type: file + processors: + - add_fields: + target: other + fields: + data: more +`, + expectedYAML: ` +inputs: + - name: No processors + type: file + processors: + - add_fields: + target: elastic + fields: + agent.id: agent-id + agent.snapshot: false + agent.version: 8.0.0 + - name: With processors + type: file + processors: + - add_fields: + target: other + fields: + data: more + - add_fields: + target: elastic + fields: + agent.id: agent-id + agent.snapshot: false + agent.version: 8.0.0 +`, + rule: &RuleList{ + Rules: []Rule{ + InjectAgentInfo(), + }, + }, + }, + "extract items from array": { givenYAML: ` streams: @@ -615,7 +660,7 @@ logs: a, err := makeASTFromYAML(test.givenYAML) require.NoError(t, err) - err = test.rule.Apply(a) + err = test.rule.Apply(FakeAgentInfo(), a) require.NoError(t, err) v := &MapVisitor{} @@ -751,3 +796,21 @@ func TestSerialization(t *testing.T) { assert.Equal(t, value, v) }) } + +type fakeAgentInfo struct{} + +func (*fakeAgentInfo) AgentID() string { + return "agent-id" +} + +func (*fakeAgentInfo) Version() string { + return "8.0.0" +} + +func (*fakeAgentInfo) Snapshot() bool { + return false +} + +func FakeAgentInfo() AgentInfo { + return &fakeAgentInfo{} +} diff --git a/x-pack/elastic-agent/spec/filebeat.yml b/x-pack/elastic-agent/spec/filebeat.yml index 1b184b10098..aa09b4f9121 100644 --- a/x-pack/elastic-agent/spec/filebeat.yml +++ b/x-pack/elastic-agent/spec/filebeat.yml @@ -87,6 +87,8 @@ rules: values: - true +- inject_agent_info: {} + - copy: from: inputs to: filebeat diff --git a/x-pack/elastic-agent/spec/metricbeat.yml b/x-pack/elastic-agent/spec/metricbeat.yml index 94b69e9a2f3..a5015a974a5 100644 --- a/x-pack/elastic-agent/spec/metricbeat.yml +++ b/x-pack/elastic-agent/spec/metricbeat.yml @@ -73,6 +73,8 @@ rules: - remove_key: key: use_output +- inject_agent_info: {} + - copy: from: inputs to: metricbeat From 5ef953bf6447ee4200f22308ac9719c97a2a8758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Tue, 6 Oct 2020 15:40:03 +0200 Subject: [PATCH 34/93] feat: add a new step to run the e2e tests for certain parts of Beats (#21100) * feat: add a new step to run the e2e tests for certain parts of Beats We are going to trigger the tests for those parts affected by the elastic-agent, filebeat, or metricbeat, because those are the ones we verify in the e2e-testing suite * chore: do not include heartbeat * feat: trigger the e2e tests * fix: use relative path * chore: use proper target branch name for PRs * chore: use different tag * fix: use proper env variable * chore: pass github checks context to downstream job * chore: revert shared lib version Co-authored-by: Victor Martinez * chore: add BASE_DIR env variable Co-authored-by: Victor Martinez * chore: remove duplicated env * ffix: add param comma separator * fix: wrong copy&paste * chore: move e2e GH check out of the release context * chore: simplify conditional logic * chore: refine execution of test suites * fix: use proper parameter name * chore: set metricbeat version * chore: remove slack notifications on PRs * chore: update parameter * chore: run multiple test suites per beat type Co-authored-by: Victor Martinez --- .ci/packaging.groovy | 50 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/.ci/packaging.groovy b/.ci/packaging.groovy index 2be78aac68f..301ead43bab 100644 --- a/.ci/packaging.groovy +++ b/.ci/packaging.groovy @@ -5,12 +5,14 @@ pipeline { agent none environment { - BASE_DIR = 'src/github.com/elastic/beats' + REPO = 'beats' + BASE_DIR = "src/github.com/elastic/${env.REPO}" JOB_GCS_BUCKET = 'beats-ci-artifacts' JOB_GCS_BUCKET_STASH = 'beats-ci-temp' JOB_GCS_CREDENTIALS = 'beats-ci-gcs-plugin' DOCKERELASTIC_SECRET = 'secret/observability-team/ci/docker-registry/prod' DOCKER_REGISTRY = 'docker.elastic.co' + GITHUB_CHECK_E2E_TESTS_NAME = 'E2E Tests' SNAPSHOT = "true" PIPELINE_LOG_LEVEL = "INFO" } @@ -119,6 +121,7 @@ pipeline { release() pushCIDockerImages() } + runE2ETestForPackages() } } stage('Package Mac OS'){ @@ -209,6 +212,25 @@ def tagAndPush(name){ } } +def runE2ETestForPackages(){ + def suite = '' + + catchError(buildResult: 'UNSTABLE', message: 'Unable to run e2e tests', stageResult: 'FAILURE') { + if ("${env.BEATS_FOLDER}" == "filebeat" || "${env.BEATS_FOLDER}" == "x-pack/filebeat") { + suite = 'helm,ingest-manager' + } else if ("${env.BEATS_FOLDER}" == "metricbeat" || "${env.BEATS_FOLDER}" == "x-pack/metricbeat") { + suite = '' + } else if ("${env.BEATS_FOLDER}" == "x-pack/elastic-agent") { + suite = 'ingest-manager' + } else { + echo("Skipping E2E tests for ${env.BEATS_FOLDER}.") + return + } + + triggerE2ETests(suite) + } +} + def release(){ withBeatsEnv(){ dir("${env.BEATS_FOLDER}") { @@ -218,6 +240,32 @@ def release(){ } } +def triggerE2ETests(String suite) { + echo("Triggering E2E tests for ${env.BEATS_FOLDER}. Test suite: ${suite}.") + + def branchName = isPR() ? "${env.CHANGE_TARGET}" : "${env.JOB_BASE_NAME}" + def e2eTestsPipeline = "e2e-tests/e2e-testing-mbp/${branchName}" + build(job: "${e2eTestsPipeline}", + parameters: [ + booleanParam(name: 'forceSkipGitChecks', value: true), + booleanParam(name: 'forceSkipPresubmit', value: true), + booleanParam(name: 'notifyOnGreenBuilds', value: !isPR()), + booleanParam(name: 'USE_CI_SNAPSHOTS', value: true), + string(name: 'ELASTIC_AGENT_VERSION', value: "pr-${env.CHANGE_ID}"), + string(name: 'METRICBEAT_VERSION', value: "pr-${env.CHANGE_ID}"), + string(name: 'runTestsSuites', value: suite), + string(name: 'GITHUB_CHECK_NAME', value: env.GITHUB_CHECK_E2E_TESTS_NAME), + string(name: 'GITHUB_CHECK_REPO', value: env.REPO), + string(name: 'GITHUB_CHECK_SHA1', value: env.GIT_BASE_COMMIT), + ], + propagate: false, + wait: false + ) + + def notifyContext = "${env.GITHUB_CHECK_E2E_TESTS_NAME} for ${env.BEATS_FOLDER}" + githubNotify(context: "${notifyContext}", description: "${notifyContext} ...", status: 'PENDING', targetUrl: "${env.JENKINS_URL}search/?q=${e2eTestsPipeline.replaceAll('/','+')}") +} + def withMacOSEnv(Closure body){ withEnvMask( vars: [ [var: "KEYCHAIN_PASS", password: getVaultSecret(secret: "secret/jenkins-ci/macos-codesign-keychain").data.password], From 9d2f3b9cfdda63c5ddba93c38ef7e24c6d516a56 Mon Sep 17 00:00:00 2001 From: Chris Mark Date: Tue, 6 Oct 2020 17:28:09 +0300 Subject: [PATCH 35/93] Move Prometheus query & remote_write to GA (#21507) --- CHANGELOG.next.asciidoc | 1 + metricbeat/docs/modules/prometheus/query.asciidoc | 2 -- metricbeat/docs/modules/prometheus/remote_write.asciidoc | 2 -- metricbeat/docs/modules_list.asciidoc | 4 ++-- metricbeat/module/prometheus/fields.go | 2 +- metricbeat/module/prometheus/query/_meta/fields.yml | 2 +- metricbeat/module/prometheus/remote_write/_meta/fields.yml | 2 +- 7 files changed, 6 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index cd1e6c50a4c..0fb2aa0f73d 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -738,6 +738,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Add overview and platform health dashboards to Cloud Foundry module. {pull}21124[21124] - Release lambda metricset in aws module as GA. {issue}21251[21251] {pull}21255[21255] - Add dashboard for pubsub metricset in googlecloud module. {pull}21326[21326] {issue}17137[17137] +- Move Prometheus query & remote_write to GA. {pull}21507[21507] - Expand unsupported option from namespace to metrics in the azure module. {pull}21486[21486] - Map cloud data filed `cloud.account.id` to azure subscription. {pull}21483[21483] {issue}21381[21381] diff --git a/metricbeat/docs/modules/prometheus/query.asciidoc b/metricbeat/docs/modules/prometheus/query.asciidoc index 72c0fde9278..ff746df44a7 100644 --- a/metricbeat/docs/modules/prometheus/query.asciidoc +++ b/metricbeat/docs/modules/prometheus/query.asciidoc @@ -5,8 +5,6 @@ This file is generated! See scripts/mage/docs_collector.go [[metricbeat-metricset-prometheus-query]] === Prometheus query metricset -beta[] - include::../../../module/prometheus/query/_meta/docs.asciidoc[] diff --git a/metricbeat/docs/modules/prometheus/remote_write.asciidoc b/metricbeat/docs/modules/prometheus/remote_write.asciidoc index 9d871805344..535fdec82e6 100644 --- a/metricbeat/docs/modules/prometheus/remote_write.asciidoc +++ b/metricbeat/docs/modules/prometheus/remote_write.asciidoc @@ -5,8 +5,6 @@ This file is generated! See scripts/mage/docs_collector.go [[metricbeat-metricset-prometheus-remote_write]] === Prometheus remote_write metricset -beta[] - include::../../../module/prometheus/remote_write/_meta/docs.asciidoc[] diff --git a/metricbeat/docs/modules_list.asciidoc b/metricbeat/docs/modules_list.asciidoc index 949657459db..5ff2305665f 100644 --- a/metricbeat/docs/modules_list.asciidoc +++ b/metricbeat/docs/modules_list.asciidoc @@ -222,8 +222,8 @@ This file is generated! See scripts/mage/docs_collector.go |<> |<> |image:./images/icon-yes.png[Prebuilt dashboards are available] | .3+| .3+| |<> -|<> beta[] -|<> beta[] +|<> +|<> |<> |image:./images/icon-yes.png[Prebuilt dashboards are available] | .4+| .4+| |<> |<> diff --git a/metricbeat/module/prometheus/fields.go b/metricbeat/module/prometheus/fields.go index e93b578c7b7..ff89ddf8ac7 100644 --- a/metricbeat/module/prometheus/fields.go +++ b/metricbeat/module/prometheus/fields.go @@ -32,5 +32,5 @@ func init() { // AssetPrometheus returns asset data. // This is the base64 encoded gzipped contents of module/prometheus. func AssetPrometheus() string { - return "eJzMkkGO2zAMRfc+xYe7G2TmAF70BAXaosuiCBT7O1ZHllSSniC3LxzHGU0yQJF2Uy75RfLxi4945rFBljTSBk5aAeYtsEH95ZKsK6CjtuKz+RQbfKwA4Js5U2grLrNDL2mEw2sVGLucfLSnCtAhiW3bFHu/b9C7oKwAYaBTNti7+Q3NfNxrg++1aqg3qAezXP+ogN4zdNqc5j4iupFX1HPYMc+9JE35nCnL5viAz9JR4BV+zEnMRcNA4QbB7RgUBx8CRmftgN6L2gY2EEI1OCG6NO0CL/1WlKX46eEirDBp95OtFeklsV3UZx4PSbpCfsfmNQpnR5r49jz1BmZR76e52u2Nuh1dzj7uz0/rh/ovoW9of02U4//G+uLCdPr1Kdh627P89VPB//Z639nqZqfyNP8Ac2qwfiVLHy5jdzRX5K9vfUURjsm4PYg3/gvR0genPivYqzNn45TyQrmD9ncAAAD//1baTA8=" + return "eJzMkk1u20AMhfc6xYO6C5wcQIueoEBbdFkUxlh6sqaZv5JUDN++kGU5im2gf5tyyTckP77hI555bFAkR9rAUSvAvAU2qD9dknUFdNRWfDGfU4P3FQB8MWcKbcUVduglRzi8VoGpK9kne6oAHbLYts2p9/sGvQvKChAGOmWDvZve0MynvTb4WquGeoN6MCv1twroPUOnzWnuI5KLvKKewo5l6iV5LOfMumyKd/goHQVe4WPJYi4ZBgo3CG7HoDj4EBCdtQN6L2ob2EAI1eCE6PK4C7z0W1Dm4qeHi7DA5N13trZKz4ntrD7zeMjSreQ7Ni+xcjbSxLfnqTcws/rnNFe7vVG30ZXi0/78tH6o/xL6hvbHSDn+b6wvLoynXx+DLbc9yZ8/rPjfXu+drW52Wp/mL2BODZav5NqHe2NvL30BEcZs3B7EG/+FZ+6DU58F69WXs21KeaH8NuvPAAAA///rUkpn" } diff --git a/metricbeat/module/prometheus/query/_meta/fields.yml b/metricbeat/module/prometheus/query/_meta/fields.yml index acbc35db449..31f29117385 100644 --- a/metricbeat/module/prometheus/query/_meta/fields.yml +++ b/metricbeat/module/prometheus/query/_meta/fields.yml @@ -2,5 +2,5 @@ type: group description: > query metricset - release: beta + release: ga fields: diff --git a/metricbeat/module/prometheus/remote_write/_meta/fields.yml b/metricbeat/module/prometheus/remote_write/_meta/fields.yml index e3b2f050a40..17f5f5fe801 100644 --- a/metricbeat/module/prometheus/remote_write/_meta/fields.yml +++ b/metricbeat/module/prometheus/remote_write/_meta/fields.yml @@ -2,5 +2,5 @@ type: group description: > remote write metrics from Prometheus server - release: beta + release: ga fields: From a2decea3c5011668156711f06fa8a1e6195bc5df Mon Sep 17 00:00:00 2001 From: kaiyan-sheng Date: Tue, 6 Oct 2020 09:01:17 -0600 Subject: [PATCH 36/93] Add support for additional fields from V2 ALB logs (#21540) * Add support for additional fields from V2 ALB logs * Add new fields as optional fields and regenerate -expected.json files * add changelog --- CHANGELOG.next.asciidoc | 1 + filebeat/docs/fields.asciidoc | 40 ++++ .../filebeat/module/aws/elb/_meta/fields.yml | 16 ++ .../module/aws/elb/ingest/pipeline.yml | 14 +- .../aws/elb/test/application-lb-http.log | 2 +- .../application-lb-http.log-expected.json | 50 ++++ .../module/aws/elb/test/example-alb-http.log | 5 +- .../test/example-alb-http.log-expected.json | 215 ++++++++++++++++++ x-pack/filebeat/module/aws/fields.go | 2 +- 9 files changed, 341 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 0fb2aa0f73d..99daa875a00 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -607,6 +607,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Add related.hosts ecs field to all modules {pull}21160[21160] - Keep cursor state between httpjson input restarts {pull}20751[20751] - Convert aws s3 to v2 input {pull}20005[20005] +- Add support for additional fields from V2 ALB logs. {pull}21540[21540] - Release Cloud Foundry input as GA. {pull}21525[21525] - New Cisco Umbrella dataset {pull}21504[21504] - New juniper.srx dataset for Juniper SRX logs. {pull}20017[20017] diff --git a/filebeat/docs/fields.asciidoc b/filebeat/docs/fields.asciidoc index e7c2d35ff37..c294aef0f79 100644 --- a/filebeat/docs/fields.asciidoc +++ b/filebeat/docs/fields.asciidoc @@ -1884,6 +1884,46 @@ type: keyword The error reason if the executed action failed. +type: keyword + +-- + +*`aws.elb.target_port`*:: ++ +-- +List of IP addresses and ports for the targets that processed this request. + + +type: keyword + +-- + +*`aws.elb.target_status_code`*:: ++ +-- +List of status codes from the responses of the targets. + + +type: keyword + +-- + +*`aws.elb.classification`*:: ++ +-- +The classification for desync mitigation. + + +type: keyword + +-- + +*`aws.elb.classification_reason`*:: ++ +-- +The classification reason code. + + type: keyword -- diff --git a/x-pack/filebeat/module/aws/elb/_meta/fields.yml b/x-pack/filebeat/module/aws/elb/_meta/fields.yml index 9499f8bbb0e..aac074e9347 100644 --- a/x-pack/filebeat/module/aws/elb/_meta/fields.yml +++ b/x-pack/filebeat/module/aws/elb/_meta/fields.yml @@ -101,3 +101,19 @@ type: keyword description: > The error reason if the executed action failed. + - name: target_port + type: keyword + description: > + List of IP addresses and ports for the targets that processed this request. + - name: target_status_code + type: keyword + description: > + List of status codes from the responses of the targets. + - name: classification + type: keyword + description: > + The classification for desync mitigation. + - name: classification_reason + type: keyword + description: > + The classification reason code. diff --git a/x-pack/filebeat/module/aws/elb/ingest/pipeline.yml b/x-pack/filebeat/module/aws/elb/ingest/pipeline.yml index de772ccdf01..8cb2a914921 100644 --- a/x-pack/filebeat/module/aws/elb/ingest/pipeline.yml +++ b/x-pack/filebeat/module/aws/elb/ingest/pipeline.yml @@ -31,7 +31,7 @@ processors: %{TIMESTAMP_ISO8601:event.start} \"(?:-|%{DATA:_tmp.actions_executed})\" \"(?:-|%{DATA:aws.elb.redirect_url})\" - \"(?:-|%{DATA:aws.elb.error.reason})\" + \"(?:-|%{DATA:aws.elb.error.reason})\"( \"(?:-|%{DATA:_tmp.target_port})\")?( \"(?:-|%{DATA:_tmp.target_status_code})\")?( \"(?:-|%{DATA:aws.elb.classification})\")?( \"(?:-|%{DATA:aws.elb.classification_reason})\")? # TCP from Network Load Balancers (v2 Load Balancers) - >- @@ -141,6 +141,18 @@ processors: separator: ',' ignore_missing: true + - split: + field: '_tmp.target_port' + target_field: 'aws.elb.target_port' + separator: ' ' + ignore_missing: true + + - split: + field: '_tmp.target_status_code' + target_field: 'aws.elb.target_status_code' + separator: ' ' + ignore_missing: true + - date: field: '_tmp.timestamp' formats: diff --git a/x-pack/filebeat/module/aws/elb/test/application-lb-http.log b/x-pack/filebeat/module/aws/elb/test/application-lb-http.log index 88ea2d75c26..5d754c4bbaa 100644 --- a/x-pack/filebeat/module/aws/elb/test/application-lb-http.log +++ b/x-pack/filebeat/module/aws/elb/test/application-lb-http.log @@ -8,4 +8,4 @@ http 2019-10-11T15:03:49.331902Z app/filebeat-aws-elb-test/c86a326e7dc14222 77.2 http 2019-10-11T15:55:09.308183Z app/filebeat-aws-elb-test/c86a326e7dc14222 77.227.156.41:37838 10.0.0.192:80 0.001 0.000 0.000 200 200 125 859 "GET http://filebeat-aws-elb-test-12030537.eu-central-1.elb.amazonaws.com:80/ HTTP/1.1" "curl/7.58.0" - - arn:aws:elasticloadbalancing:eu-central-1:627959692251:targetgroup/test-lb-instances/8f04c4fe71f5f794 "Root=1-5da0a5dd-4d9a423a0e9a782fe2f390af" "-" "-" 0 2019-10-11T15:55:09.307000Z "forward" "-" "-" http 2019-10-11T15:55:11.354283Z app/filebeat-aws-elb-test/c86a326e7dc14222 77.227.156.41:37850 10.0.1.107:80 0.001 0.001 0.000 200 200 125 859 "GET http://filebeat-aws-elb-test-12030537.eu-central-1.elb.amazonaws.com:80/ HTTP/1.1" "curl/7.58.0" - - arn:aws:elasticloadbalancing:eu-central-1:627959692251:targetgroup/test-lb-instances/8f04c4fe71f5f794 "Root=1-5da0a5df-7d64cabe9955b4df9acc800a" "-" "-" 0 2019-10-11T15:55:11.352000Z "forward" "-" "-" http 2019-10-11T15:55:11.987940Z app/filebeat-aws-elb-test/c86a326e7dc14222 77.227.156.41:37856 10.0.0.192:80 0.000 0.001 0.000 200 200 125 859 "GET http://filebeat-aws-elb-test-12030537.eu-central-1.elb.amazonaws.com:80/ HTTP/1.1" "curl/7.58.0" - - arn:aws:elasticloadbalancing:eu-central-1:627959692251:targetgroup/test-lb-instances/8f04c4fe71f5f794 "Root=1-5da0a5df-7c958e828ff43b63d0e0fac4" "-" "-" 0 2019-10-11T15:55:11.987000Z "forward" "-" "-" - +http 2018-07-02T22:23:00.186641Z app/my-loadbalancer/50dc6c495c0c9188 192.168.131.39:2817 10.0.0.1:80 0.000 0.001 0.000 200 200 34 366 "GET http://www.example.com:80/ HTTP/1.1" "curl/7.46.0" - - arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067 "Root=1-58337262-36d228ad5d99923122bbe354" "-" "-" 0 2018-07-02T22:22:48.364000Z "forward,redirect" "-" "-" "10.0.0.1:80" "200" "-" "-" diff --git a/x-pack/filebeat/module/aws/elb/test/application-lb-http.log-expected.json b/x-pack/filebeat/module/aws/elb/test/application-lb-http.log-expected.json index 28e1564e928..3682fb6520e 100644 --- a/x-pack/filebeat/module/aws/elb/test/application-lb-http.log-expected.json +++ b/x-pack/filebeat/module/aws/elb/test/application-lb-http.log-expected.json @@ -500,5 +500,55 @@ ], "tracing.trace.id": "Root=1-5da0a5df-7c958e828ff43b63d0e0fac4", "user_agent.original": "curl/7.58.0" + }, + { + "@timestamp": "2018-07-02T22:23:00.186Z", + "aws.elb.action_executed": [ + "forward", + "redirect" + ], + "aws.elb.backend.http.response.status_code": 200, + "aws.elb.backend.ip": "10.0.0.1", + "aws.elb.backend.port": "80", + "aws.elb.backend_processing_time.sec": 0.001, + "aws.elb.matched_rule_priority": "0", + "aws.elb.name": "app/my-loadbalancer/50dc6c495c0c9188", + "aws.elb.protocol": "http", + "aws.elb.request_processing_time.sec": 0.0, + "aws.elb.response_processing_time.sec": 0.0, + "aws.elb.target_group.arn": "arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067", + "aws.elb.target_port": [ + "10.0.0.1:80" + ], + "aws.elb.target_status_code": [ + "200" + ], + "aws.elb.trace_id": "Root=1-58337262-36d228ad5d99923122bbe354", + "aws.elb.type": "http", + "cloud.provider": "aws", + "event.category": "web", + "event.dataset": "aws.elb", + "event.end": "2018-07-02T22:23:00.186Z", + "event.kind": "event", + "event.module": "aws", + "event.outcome": "success", + "event.start": "2018-07-02T22:22:48.364000Z", + "fileset.name": "elb", + "http.request.body.bytes": 34, + "http.request.method": "GET", + "http.request.referrer": "http://www.example.com:80/", + "http.response.body.bytes": 366, + "http.response.status_code": 200, + "http.version": "1.1", + "input.type": "log", + "log.offset": 4431, + "service.type": "aws", + "source.ip": "192.168.131.39", + "source.port": "2817", + "tags": [ + "forwarded" + ], + "tracing.trace.id": "Root=1-58337262-36d228ad5d99923122bbe354", + "user_agent.original": "curl/7.46.0" } ] \ No newline at end of file diff --git a/x-pack/filebeat/module/aws/elb/test/example-alb-http.log b/x-pack/filebeat/module/aws/elb/test/example-alb-http.log index 9e4526d2d61..94c0ec1360b 100644 --- a/x-pack/filebeat/module/aws/elb/test/example-alb-http.log +++ b/x-pack/filebeat/module/aws/elb/test/example-alb-http.log @@ -7,4 +7,7 @@ http 2018-11-30T22:23:00.186641Z app/my-loadbalancer/50dc6c495c0c9188 192.168.13 http 2018-11-30T22:23:00.186641Z app/my-loadbalancer/50dc6c495c0c9188 192.168.131.39:2817 - 0.000 0.001 0.000 502 - 34 366 "GET http://www.example.com:80/ HTTP/1.1" "curl/7.46.0" - - arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067 "Root=1-58337364-23a8c76965a2ef7629b185e3" "-" "-" 0 2018-11-30T22:22:48.364000Z "forward" "-" "LambdaInvalidResponse" http 2018-11-30T22:23:00.186641Z app/my-loadbalancer/50dc6c495c0c9188 192.168.131.39:2817 - -1 -1 -1 400 - 0 0 "- http://www.example.com:80- -" "-" - - - "-" "-" "-" 0 2018-11-30T22:22:48.364000Z "-" "-" "-" http 2018-11-30T22:23:00.186641Z app/my-loadbalancer/50dc6c495c0c9188 192.168.131.39:2817 - -1 -1 -1 400 - 0 0 "- - -" "-" - - - "-" "-" "-" 0 2018-11-30T22:22:48.364000Z "-" "-" "-" - +h2 2018-07-02T22:23:00.186641Z app/my-loadbalancer/50dc6c495c0c9188 10.0.1.252:48160 10.0.0.66:9000 0.000 0.002 0.000 200 200 5 257 "GET https://10.0.2.105:773/ HTTP/2.0" "curl/7.46.0" ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067 "Root=1-58337327-72bd00b0343d75b906739c42" "-" "-" 1 2018-07-02T22:22:48.364000Z "redirect" "https://example.com:80/" "-" "10.0.0.66:9000" "200" "-" "-" +https 2018-07-02T22:23:00.186641Z app/my-loadbalancer/50dc6c495c0c9188 192.168.131.39:2817 10.0.0.1:80 0.086 0.048 0.037 200 200 0 57 "GET https://www.example.com:443/ HTTP/1.1" "curl/7.46.0" ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067 "Root=1-58337281-1d84f3d73c47ec4e58577259" "www.example.com" "arn:aws:acm:us-east-2:123456789012:certificate/12345678-1234-1234-1234-123456789012" 1 2018-07-02T22:22:48.364000Z "authenticate,forward" "-" "-" "10.0.0.1:80" "200" "-" "-" +ws 2018-07-02T22:23:00.186641Z app/my-loadbalancer/50dc6c495c0c9188 10.0.0.140:40914 10.0.1.192:8010 0.001 0.003 0.000 101 101 218 587 "GET http://10.0.0.30:80/ HTTP/1.1" "-" - - arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067 "Root=1-58337364-23a8c76965a2ef7629b185e3" "-" "-" 1 2018-07-02T22:22:48.364000Z "forward" "-" "-" "10.0.1.192:8010" "101" "-" "-" +wss 2018-07-02T22:23:00.186641Z app/my-loadbalancer/50dc6c495c0c9188 10.0.0.140:44244 10.0.0.171:8010 0.000 0.001 0.000 101 101 218 786 "GET https://10.0.0.30:443/ HTTP/1.1" "-" ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067 "Root=1-58337364-23a8c76965a2ef7629b185e3" "-" "-" 1 2018-07-02T22:22:48.364000Z "forward" "-" "-" "10.0.0.171:8010" "101" "-" "-" diff --git a/x-pack/filebeat/module/aws/elb/test/example-alb-http.log-expected.json b/x-pack/filebeat/module/aws/elb/test/example-alb-http.log-expected.json index eb1fad5f705..2c1490142fa 100644 --- a/x-pack/filebeat/module/aws/elb/test/example-alb-http.log-expected.json +++ b/x-pack/filebeat/module/aws/elb/test/example-alb-http.log-expected.json @@ -368,5 +368,220 @@ ], "tracing.trace.id": "-", "user_agent.original": "-" + }, + { + "@timestamp": "2018-07-02T22:23:00.186Z", + "aws.elb.action_executed": [ + "redirect" + ], + "aws.elb.backend.http.response.status_code": 200, + "aws.elb.backend.ip": "10.0.0.66", + "aws.elb.backend.port": "9000", + "aws.elb.backend_processing_time.sec": 0.002, + "aws.elb.matched_rule_priority": "1", + "aws.elb.name": "app/my-loadbalancer/50dc6c495c0c9188", + "aws.elb.protocol": "http", + "aws.elb.redirect_url": "https://example.com:80/", + "aws.elb.request_processing_time.sec": 0.0, + "aws.elb.response_processing_time.sec": 0.0, + "aws.elb.ssl_cipher": "ECDHE-RSA-AES128-GCM-SHA256", + "aws.elb.ssl_protocol": "TLSv1.2", + "aws.elb.target_group.arn": "arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067", + "aws.elb.target_port": [ + "10.0.0.66:9000" + ], + "aws.elb.target_status_code": [ + "200" + ], + "aws.elb.trace_id": "Root=1-58337327-72bd00b0343d75b906739c42", + "aws.elb.type": "h2", + "cloud.provider": "aws", + "event.category": "web", + "event.dataset": "aws.elb", + "event.end": "2018-07-02T22:23:00.186Z", + "event.kind": "event", + "event.module": "aws", + "event.outcome": "success", + "event.start": "2018-07-02T22:22:48.364000Z", + "fileset.name": "elb", + "http.request.body.bytes": 5, + "http.request.method": "GET", + "http.request.referrer": "https://10.0.2.105:773/", + "http.response.body.bytes": 257, + "http.response.status_code": 200, + "http.version": "2.0", + "input.type": "log", + "log.offset": 3284, + "service.type": "aws", + "source.ip": "10.0.1.252", + "source.port": "48160", + "tags": [ + "forwarded" + ], + "tls.cipher": "ECDHE-RSA-AES128-GCM-SHA256", + "tls.version": "1.2", + "tls.version_protocol": "tls", + "tracing.trace.id": "Root=1-58337327-72bd00b0343d75b906739c42", + "user_agent.original": "curl/7.46.0" + }, + { + "@timestamp": "2018-07-02T22:23:00.186Z", + "aws.elb.action_executed": [ + "authenticate", + "forward" + ], + "aws.elb.backend.http.response.status_code": 200, + "aws.elb.backend.ip": "10.0.0.1", + "aws.elb.backend.port": "80", + "aws.elb.backend_processing_time.sec": 0.048, + "aws.elb.chosen_cert.arn": "arn:aws:acm:us-east-2:123456789012:certificate/12345678-1234-1234-1234-123456789012", + "aws.elb.matched_rule_priority": "1", + "aws.elb.name": "app/my-loadbalancer/50dc6c495c0c9188", + "aws.elb.protocol": "http", + "aws.elb.request_processing_time.sec": 0.086, + "aws.elb.response_processing_time.sec": 0.037, + "aws.elb.ssl_cipher": "ECDHE-RSA-AES128-GCM-SHA256", + "aws.elb.ssl_protocol": "TLSv1.2", + "aws.elb.target_group.arn": "arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067", + "aws.elb.target_port": [ + "10.0.0.1:80" + ], + "aws.elb.target_status_code": [ + "200" + ], + "aws.elb.trace_id": "Root=1-58337281-1d84f3d73c47ec4e58577259", + "aws.elb.type": "https", + "cloud.provider": "aws", + "destination.domain": "www.example.com", + "event.category": "web", + "event.dataset": "aws.elb", + "event.end": "2018-07-02T22:23:00.186Z", + "event.kind": "event", + "event.module": "aws", + "event.outcome": "success", + "event.start": "2018-07-02T22:22:48.364000Z", + "fileset.name": "elb", + "http.request.body.bytes": 0, + "http.request.method": "GET", + "http.request.referrer": "https://www.example.com:443/", + "http.response.body.bytes": 57, + "http.response.status_code": 200, + "http.version": "1.1", + "input.type": "log", + "log.offset": 3750, + "service.type": "aws", + "source.ip": "192.168.131.39", + "source.port": "2817", + "tags": [ + "forwarded" + ], + "tls.cipher": "ECDHE-RSA-AES128-GCM-SHA256", + "tls.version": "1.2", + "tls.version_protocol": "tls", + "tracing.trace.id": "Root=1-58337281-1d84f3d73c47ec4e58577259", + "user_agent.original": "curl/7.46.0" + }, + { + "@timestamp": "2018-07-02T22:23:00.186Z", + "aws.elb.action_executed": [ + "forward" + ], + "aws.elb.backend.http.response.status_code": 101, + "aws.elb.backend.ip": "10.0.1.192", + "aws.elb.backend.port": "8010", + "aws.elb.backend_processing_time.sec": 0.003, + "aws.elb.matched_rule_priority": "1", + "aws.elb.name": "app/my-loadbalancer/50dc6c495c0c9188", + "aws.elb.protocol": "http", + "aws.elb.request_processing_time.sec": 0.001, + "aws.elb.response_processing_time.sec": 0.0, + "aws.elb.target_group.arn": "arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067", + "aws.elb.target_port": [ + "10.0.1.192:8010" + ], + "aws.elb.target_status_code": [ + "101" + ], + "aws.elb.trace_id": "Root=1-58337364-23a8c76965a2ef7629b185e3", + "aws.elb.type": "ws", + "cloud.provider": "aws", + "event.category": "web", + "event.dataset": "aws.elb", + "event.end": "2018-07-02T22:23:00.186Z", + "event.kind": "event", + "event.module": "aws", + "event.outcome": "success", + "event.start": "2018-07-02T22:22:48.364000Z", + "fileset.name": "elb", + "http.request.body.bytes": 218, + "http.request.method": "GET", + "http.request.referrer": "http://10.0.0.30:80/", + "http.response.body.bytes": 587, + "http.response.status_code": 101, + "http.version": "1.1", + "input.type": "log", + "log.offset": 4306, + "service.type": "aws", + "source.ip": "10.0.0.140", + "source.port": "40914", + "tags": [ + "forwarded" + ], + "tracing.trace.id": "Root=1-58337364-23a8c76965a2ef7629b185e3", + "user_agent.original": "-" + }, + { + "@timestamp": "2018-07-02T22:23:00.186Z", + "aws.elb.action_executed": [ + "forward" + ], + "aws.elb.backend.http.response.status_code": 101, + "aws.elb.backend.ip": "10.0.0.171", + "aws.elb.backend.port": "8010", + "aws.elb.backend_processing_time.sec": 0.001, + "aws.elb.matched_rule_priority": "1", + "aws.elb.name": "app/my-loadbalancer/50dc6c495c0c9188", + "aws.elb.protocol": "http", + "aws.elb.request_processing_time.sec": 0.0, + "aws.elb.response_processing_time.sec": 0.0, + "aws.elb.ssl_cipher": "ECDHE-RSA-AES128-GCM-SHA256", + "aws.elb.ssl_protocol": "TLSv1.2", + "aws.elb.target_group.arn": "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067", + "aws.elb.target_port": [ + "10.0.0.171:8010" + ], + "aws.elb.target_status_code": [ + "101" + ], + "aws.elb.trace_id": "Root=1-58337364-23a8c76965a2ef7629b185e3", + "aws.elb.type": "wss", + "cloud.provider": "aws", + "event.category": "web", + "event.dataset": "aws.elb", + "event.end": "2018-07-02T22:23:00.186Z", + "event.kind": "event", + "event.module": "aws", + "event.outcome": "success", + "event.start": "2018-07-02T22:22:48.364000Z", + "fileset.name": "elb", + "http.request.body.bytes": 218, + "http.request.method": "GET", + "http.request.referrer": "https://10.0.0.30:443/", + "http.response.body.bytes": 786, + "http.response.status_code": 101, + "http.version": "1.1", + "input.type": "log", + "log.offset": 4708, + "service.type": "aws", + "source.ip": "10.0.0.140", + "source.port": "44244", + "tags": [ + "forwarded" + ], + "tls.cipher": "ECDHE-RSA-AES128-GCM-SHA256", + "tls.version": "1.2", + "tls.version_protocol": "tls", + "tracing.trace.id": "Root=1-58337364-23a8c76965a2ef7629b185e3", + "user_agent.original": "-" } ] \ No newline at end of file diff --git a/x-pack/filebeat/module/aws/fields.go b/x-pack/filebeat/module/aws/fields.go index 352932f1b1c..e8968b65e8e 100644 --- a/x-pack/filebeat/module/aws/fields.go +++ b/x-pack/filebeat/module/aws/fields.go @@ -19,5 +19,5 @@ func init() { // AssetAws returns asset data. // This is the base64 encoded gzipped contents of module/aws. func AssetAws() string { - return "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" + return "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" } From bf971f3919f73b1885abffc2683cbcd99af1822b Mon Sep 17 00:00:00 2001 From: Brandon Morelli Date: Tue, 6 Oct 2020 08:09:24 -0700 Subject: [PATCH 37/93] docs: update generate_fields_docs.py (#21359) --- auditbeat/docs/fields.asciidoc | 9 +++++++- filebeat/docs/fields.asciidoc | 9 +++++++- heartbeat/docs/fields.asciidoc | 9 +++++++- journalbeat/docs/fields.asciidoc | 9 +++++++- libbeat/scripts/generate_fields_docs.py | 26 ++++++++++++++++++++++-- metricbeat/docs/fields.asciidoc | 9 +++++++- packetbeat/docs/fields.asciidoc | 9 +++++++- winlogbeat/docs/fields.asciidoc | 9 +++++++- x-pack/functionbeat/docs/fields.asciidoc | 9 +++++++- 9 files changed, 88 insertions(+), 10 deletions(-) diff --git a/auditbeat/docs/fields.asciidoc b/auditbeat/docs/fields.asciidoc index 7ba194357ee..cb95eb4da12 100644 --- a/auditbeat/docs/fields.asciidoc +++ b/auditbeat/docs/fields.asciidoc @@ -2914,8 +2914,15 @@ type: object [[exported-fields-ecs]] == ECS fields -ECS Fields. +This section defines Elastic Common Schema (ECS) fields—a common set of fields +to be used when storing event data in {es}. + +This is an exhaustive list, and fields listed here are not necessarily used by {beatname_uc}. +The goal of ECS is to enable and encourage users of {es} to normalize their event data, +so that they can better analyze, visualize, and correlate the data represented in their events. + +See the {ecs-ref}[ECS reference] for more information. *`@timestamp`*:: + diff --git a/filebeat/docs/fields.asciidoc b/filebeat/docs/fields.asciidoc index c294aef0f79..b4f6a158ad7 100644 --- a/filebeat/docs/fields.asciidoc +++ b/filebeat/docs/fields.asciidoc @@ -44228,8 +44228,15 @@ type: object [[exported-fields-ecs]] == ECS fields -ECS Fields. +This section defines Elastic Common Schema (ECS) fields—a common set of fields +to be used when storing event data in {es}. + +This is an exhaustive list, and fields listed here are not necessarily used by {beatname_uc}. +The goal of ECS is to enable and encourage users of {es} to normalize their event data, +so that they can better analyze, visualize, and correlate the data represented in their events. + +See the {ecs-ref}[ECS reference] for more information. *`@timestamp`*:: + diff --git a/heartbeat/docs/fields.asciidoc b/heartbeat/docs/fields.asciidoc index 20e797faf1a..e80238bca4c 100644 --- a/heartbeat/docs/fields.asciidoc +++ b/heartbeat/docs/fields.asciidoc @@ -361,8 +361,15 @@ type: object [[exported-fields-ecs]] == ECS fields -ECS Fields. +This section defines Elastic Common Schema (ECS) fields—a common set of fields +to be used when storing event data in {es}. + +This is an exhaustive list, and fields listed here are not necessarily used by {beatname_uc}. +The goal of ECS is to enable and encourage users of {es} to normalize their event data, +so that they can better analyze, visualize, and correlate the data represented in their events. + +See the {ecs-ref}[ECS reference] for more information. *`@timestamp`*:: + diff --git a/journalbeat/docs/fields.asciidoc b/journalbeat/docs/fields.asciidoc index bb7627508a4..57f97a4162e 100644 --- a/journalbeat/docs/fields.asciidoc +++ b/journalbeat/docs/fields.asciidoc @@ -914,8 +914,15 @@ type: object [[exported-fields-ecs]] == ECS fields -ECS Fields. +This section defines Elastic Common Schema (ECS) fields—a common set of fields +to be used when storing event data in {es}. + +This is an exhaustive list, and fields listed here are not necessarily used by {beatname_uc}. +The goal of ECS is to enable and encourage users of {es} to normalize their event data, +so that they can better analyze, visualize, and correlate the data represented in their events. + +See the {ecs-ref}[ECS reference] for more information. *`@timestamp`*:: + diff --git a/libbeat/scripts/generate_fields_docs.py b/libbeat/scripts/generate_fields_docs.py index 89a80d83d27..8e0a7c77c32 100644 --- a/libbeat/scripts/generate_fields_docs.py +++ b/libbeat/scripts/generate_fields_docs.py @@ -1,6 +1,7 @@ import argparse from collections import OrderedDict import os +import re import yaml @@ -20,11 +21,24 @@ def document_fields(output, section, sections, path): output.write("[float]\n") if "description" in section: - if "anchor" in section: + if "anchor" in section and section["name"] == "ECS": output.write("== {} fields\n\n".format(section["name"])) + output.write(""" +This section defines Elastic Common Schema (ECS) fields—a common set of fields +to be used when storing event data in {es}. + +This is an exhaustive list, and fields listed here are not necessarily used by {beatname_uc}. +The goal of ECS is to enable and encourage users of {es} to normalize their event data, +so that they can better analyze, visualize, and correlate the data represented in their events. + +See the {ecs-ref}[ECS reference] for more information. +""") + elif "anchor" in section: + output.write("== {} fields\n\n".format(section["name"])) + output.write("{}\n\n".format(section["description"])) else: output.write("=== {}\n\n".format(section["name"])) - output.write("{}\n\n".format(section["description"])) + output.write("{}\n\n".format(section["description"])) if "fields" not in section or not section["fields"]: return @@ -70,6 +84,14 @@ def document_field(output, field, field_path): if "path" in field: output.write("alias to: {}\n\n".format(field["path"])) + # For Apm-Server docs only + # Assign an ECS badge for fields containing "overwrite" + if beat_title == "Apm-Server": + if "overwrite" in field: + # And it's not a Kubernetes field + if re.match("^kubernetes.*", field["field_path"]) is None: + output.write("{yes-icon} {ecs-ref}[ECS] field.\n\n") + if "index" in field: if not field["index"]: output.write("{}\n\n".format("Field is not indexed.")) diff --git a/metricbeat/docs/fields.asciidoc b/metricbeat/docs/fields.asciidoc index 26e83c19050..dc7c0c45d3e 100644 --- a/metricbeat/docs/fields.asciidoc +++ b/metricbeat/docs/fields.asciidoc @@ -9371,8 +9371,15 @@ Stats collected from Dropwizard. [[exported-fields-ecs]] == ECS fields -ECS Fields. +This section defines Elastic Common Schema (ECS) fields—a common set of fields +to be used when storing event data in {es}. + +This is an exhaustive list, and fields listed here are not necessarily used by {beatname_uc}. +The goal of ECS is to enable and encourage users of {es} to normalize their event data, +so that they can better analyze, visualize, and correlate the data represented in their events. + +See the {ecs-ref}[ECS reference] for more information. *`@timestamp`*:: + diff --git a/packetbeat/docs/fields.asciidoc b/packetbeat/docs/fields.asciidoc index 14ed56f1578..22d2b406bf9 100644 --- a/packetbeat/docs/fields.asciidoc +++ b/packetbeat/docs/fields.asciidoc @@ -2128,8 +2128,15 @@ type: object [[exported-fields-ecs]] == ECS fields -ECS Fields. +This section defines Elastic Common Schema (ECS) fields—a common set of fields +to be used when storing event data in {es}. + +This is an exhaustive list, and fields listed here are not necessarily used by {beatname_uc}. +The goal of ECS is to enable and encourage users of {es} to normalize their event data, +so that they can better analyze, visualize, and correlate the data represented in their events. + +See the {ecs-ref}[ECS reference] for more information. *`@timestamp`*:: + diff --git a/winlogbeat/docs/fields.asciidoc b/winlogbeat/docs/fields.asciidoc index d0b1a0a1473..cc84c0fad8a 100644 --- a/winlogbeat/docs/fields.asciidoc +++ b/winlogbeat/docs/fields.asciidoc @@ -220,8 +220,15 @@ type: object [[exported-fields-ecs]] == ECS fields -ECS Fields. +This section defines Elastic Common Schema (ECS) fields—a common set of fields +to be used when storing event data in {es}. + +This is an exhaustive list, and fields listed here are not necessarily used by {beatname_uc}. +The goal of ECS is to enable and encourage users of {es} to normalize their event data, +so that they can better analyze, visualize, and correlate the data represented in their events. + +See the {ecs-ref}[ECS reference] for more information. *`@timestamp`*:: + diff --git a/x-pack/functionbeat/docs/fields.asciidoc b/x-pack/functionbeat/docs/fields.asciidoc index 73c93e39a61..7f4aa3d5aaf 100644 --- a/x-pack/functionbeat/docs/fields.asciidoc +++ b/x-pack/functionbeat/docs/fields.asciidoc @@ -216,8 +216,15 @@ type: object [[exported-fields-ecs]] == ECS fields -ECS Fields. +This section defines Elastic Common Schema (ECS) fields—a common set of fields +to be used when storing event data in {es}. + +This is an exhaustive list, and fields listed here are not necessarily used by {beatname_uc}. +The goal of ECS is to enable and encourage users of {es} to normalize their event data, +so that they can better analyze, visualize, and correlate the data represented in their events. + +See the {ecs-ref}[ECS reference] for more information. *`@timestamp`*:: + From 1ce876d1dcf1ada555bbbba11382b63ec8cda95d Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Tue, 6 Oct 2020 16:27:07 +0100 Subject: [PATCH 38/93] [CI] Setup git config globally (#21562) --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 08853ab1453..df75ad2ccd1 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -265,8 +265,8 @@ def withBeatsEnv(Map args = [:], Closure body) { // See https://github.com/elastic/beats/issues/17787. sh(label: 'check git config', script: ''' if [ -z "$(git config --get user.email)" ]; then - git config user.email "beatsmachine@users.noreply.github.com" - git config user.name "beatsmachine" + git config --global user.email "beatsmachine@users.noreply.github.com" + git config --global user.name "beatsmachine" fi''') } try { From d6ee968b44dbb9413c8472ca1712914d192cbc00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20P=C3=A9rez-Aradros=20Herce?= Date: Tue, 6 Oct 2020 18:36:24 +0200 Subject: [PATCH 39/93] Add kubernetes composable inputs provider (#21480) * Add kubernetes composable inputs provider --- x-pack/elastic-agent/CHANGELOG.next.asciidoc | 1 + x-pack/elastic-agent/pkg/agent/cmd/include.go | 1 + .../composable/providers/kubernetes/config.go | 31 +++ .../providers/kubernetes/kubernetes.go | 215 ++++++++++++++++++ 4 files changed, 248 insertions(+) create mode 100644 x-pack/elastic-agent/pkg/composable/providers/kubernetes/config.go create mode 100644 x-pack/elastic-agent/pkg/composable/providers/kubernetes/kubernetes.go diff --git a/x-pack/elastic-agent/CHANGELOG.next.asciidoc b/x-pack/elastic-agent/CHANGELOG.next.asciidoc index 639b1dbad17..897b64c517e 100644 --- a/x-pack/elastic-agent/CHANGELOG.next.asciidoc +++ b/x-pack/elastic-agent/CHANGELOG.next.asciidoc @@ -28,6 +28,7 @@ - Add support for EQL based condition on inputs {pull}20994[20994] - Send `fleet.host.id` to Endpoint Security {pull}21042[21042] - Add `install` and `uninstall` subcommands {pull}21206[21206] +- Add `kubernetes` composable dynamic provider. {pull}21480[21480] - Send updating state {pull}21461[21461] - Add `elastic.agent.id` and `elastic.agent.version` to published events from filebeat and metricbeat {pull}21543[21543] - Add `upgrade` subcommand to perform upgrade of installed Elastic Agent {pull}21425[21425] diff --git a/x-pack/elastic-agent/pkg/agent/cmd/include.go b/x-pack/elastic-agent/pkg/agent/cmd/include.go index 87506b88415..b955c85418f 100644 --- a/x-pack/elastic-agent/pkg/agent/cmd/include.go +++ b/x-pack/elastic-agent/pkg/agent/cmd/include.go @@ -10,6 +10,7 @@ import ( _ "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/composable/providers/docker" _ "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/composable/providers/env" _ "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/composable/providers/host" + _ "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/composable/providers/kubernetes" _ "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/composable/providers/local" _ "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/composable/providers/localdynamic" _ "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/composable/providers/path" diff --git a/x-pack/elastic-agent/pkg/composable/providers/kubernetes/config.go b/x-pack/elastic-agent/pkg/composable/providers/kubernetes/config.go new file mode 100644 index 00000000000..200bedbe878 --- /dev/null +++ b/x-pack/elastic-agent/pkg/composable/providers/kubernetes/config.go @@ -0,0 +1,31 @@ +// 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. + +// TODO review the need for this +// +build linux darwin windows + +package kubernetes + +import ( + "time" +) + +// Config for kubernetes provider +type Config struct { + KubeConfig string `config:"kube_config"` + SyncPeriod time.Duration `config:"sync_period"` + CleanupTimeout time.Duration `config:"cleanup_timeout" validate:"positive"` + + // Needed when resource is a pod + Node string `config:"node"` + + // Scope of the provider (cluster or node) + Scope string `config:"scope"` +} + +// InitDefaults initializes the default values for the config. +func (c *Config) InitDefaults() { + c.SyncPeriod = 10 * time.Minute + c.CleanupTimeout = 60 * time.Second +} diff --git a/x-pack/elastic-agent/pkg/composable/providers/kubernetes/kubernetes.go b/x-pack/elastic-agent/pkg/composable/providers/kubernetes/kubernetes.go new file mode 100644 index 00000000000..c777c8a05ce --- /dev/null +++ b/x-pack/elastic-agent/pkg/composable/providers/kubernetes/kubernetes.go @@ -0,0 +1,215 @@ +// 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 kubernetes + +import ( + "fmt" + "time" + + "github.com/elastic/beats/v7/libbeat/common/kubernetes" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/composable" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/config" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger" +) + +func init() { + composable.Providers.AddDynamicProvider("kubernetes", DynamicProviderBuilder) +} + +type dynamicProvider struct { + logger *logger.Logger + config *Config +} + +type eventWatcher struct { + logger *logger.Logger + cleanupTimeout time.Duration + comm composable.DynamicProviderComm +} + +// DynamicProviderBuilder builds the dynamic provider. +func DynamicProviderBuilder(logger *logger.Logger, c *config.Config) (composable.DynamicProvider, error) { + var cfg Config + if c == nil { + c = config.New() + } + err := c.Unpack(&cfg) + if err != nil { + return nil, errors.New(err, "failed to unpack configuration") + } + return &dynamicProvider{logger, &cfg}, nil +} + +// Run runs the environment context provider. +func (p *dynamicProvider) Run(comm composable.DynamicProviderComm) error { + client, err := kubernetes.GetKubernetesClient(p.config.KubeConfig) + if err != nil { + // info only; return nil (do nothing) + p.logger.Debugf("Kubernetes provider skipped, unable to connect: %s", err) + return nil + } + + // Ensure that node is set correctly whenever the scope is set to "node". Make sure that node is empty + // when cluster scope is enforced. + if p.config.Scope == "node" { + p.config.Node = kubernetes.DiscoverKubernetesNode(p.logger, p.config.Node, kubernetes.IsInCluster(p.config.KubeConfig), client) + } else { + p.config.Node = "" + } + p.logger.Infof("Kubernetes provider started with %s scope", p.config.Scope) + p.logger.Debugf("Initializing Kubernetes watcher using node: %v", p.config.Node) + + watcher, err := kubernetes.NewWatcher(client, &kubernetes.Pod{}, kubernetes.WatchOptions{ + SyncTimeout: p.config.SyncPeriod, + Node: p.config.Node, + //Namespace: p.config.Namespace, + }, nil) + if err != nil { + return errors.New(err, "couldn't create kubernetes watcher") + } + + watcher.AddEventHandler(&eventWatcher{p.logger, p.config.CleanupTimeout, comm}) + + err = watcher.Start() + if err != nil { + return errors.New(err, "couldn't start kubernetes watcher") + } + + return nil +} + +func (p *eventWatcher) emitRunning(pod *kubernetes.Pod) { + mapping := map[string]interface{}{ + "namespace": pod.GetNamespace(), + "pod": map[string]interface{}{ + "uid": string(pod.GetUID()), + "name": pod.GetName(), + "labels": pod.GetLabels(), + "ip": pod.Status.PodIP, + }, + } + + processors := []map[string]interface{}{ + { + "add_fields": map[string]interface{}{ + "fields": mapping, + "target": "kubernetes", + }, + }, + } + + // Emit the pod + // We emit Pod + containers to ensure that configs matching Pod only + // get Pod metadata (not specific to any container) + p.comm.AddOrUpdate(string(pod.GetUID()), mapping, processors) + + // Emit all containers in the pod + p.emitContainers(pod, pod.Spec.Containers, pod.Status.ContainerStatuses) + + // TODO deal with init containers stopping after initialization + p.emitContainers(pod, pod.Spec.InitContainers, pod.Status.InitContainerStatuses) +} + +func (p *eventWatcher) emitContainers(pod *kubernetes.Pod, containers []kubernetes.Container, containerstatuses []kubernetes.PodContainerStatus) { + // Collect all runtimes from status information. + containerIDs := map[string]string{} + runtimes := map[string]string{} + for _, c := range containerstatuses { + cid, runtime := kubernetes.ContainerIDWithRuntime(c) + containerIDs[c.Name] = cid + runtimes[c.Name] = runtime + } + + for _, c := range containers { + // If it doesn't have an ID, container doesn't exist in + // the runtime, emit only an event if we are stopping, so + // we are sure of cleaning up configurations. + cid := containerIDs[c.Name] + if cid == "" { + continue + } + + // ID is the combination of pod UID + container name + eventID := fmt.Sprintf("%s.%s", pod.GetObjectMeta().GetUID(), c.Name) + + mapping := map[string]interface{}{ + "namespace": pod.GetNamespace(), + "pod": map[string]interface{}{ + "uid": string(pod.GetUID()), + "name": pod.GetName(), + "labels": pod.GetLabels(), + "ip": pod.Status.PodIP, + }, + "container": map[string]interface{}{ + "id": cid, + "name": c.Name, + "image": c.Image, + "runtime": runtimes[c.Name], + }, + } + + processors := []map[string]interface{}{ + { + "add_fields": map[string]interface{}{ + "fields": mapping, + "target": "kubernetes", + }, + }, + } + + // Emit the container + p.comm.AddOrUpdate(eventID, mapping, processors) + } +} + +func (p *eventWatcher) emitStopped(pod *kubernetes.Pod) { + p.comm.Remove(string(pod.GetUID())) + + for _, c := range pod.Spec.Containers { + // ID is the combination of pod UID + container name + eventID := fmt.Sprintf("%s.%s", pod.GetObjectMeta().GetUID(), c.Name) + p.comm.Remove(eventID) + } + + for _, c := range pod.Spec.InitContainers { + // ID is the combination of pod UID + container name + eventID := fmt.Sprintf("%s.%s", pod.GetObjectMeta().GetUID(), c.Name) + p.comm.Remove(eventID) + } +} + +// OnAdd ensures processing of pod objects that are newly added +func (p *eventWatcher) OnAdd(obj interface{}) { + p.logger.Debugf("pod add: %+v", obj) + p.emitRunning(obj.(*kubernetes.Pod)) +} + +// OnUpdate emits events for a given pod depending on the state of the pod, +// if it is terminating, a stop event is scheduled, if not, a stop and a start +// events are sent sequentially to recreate the resources assotiated to the pod. +func (p *eventWatcher) OnUpdate(obj interface{}) { + pod := obj.(*kubernetes.Pod) + + p.logger.Debugf("pod update for pod: %+v, status: %+v", pod.Name, pod.Status.Phase) + switch pod.Status.Phase { + case kubernetes.PodSucceeded, kubernetes.PodFailed: + time.AfterFunc(p.cleanupTimeout, func() { p.emitStopped(pod) }) + return + case kubernetes.PodPending: + p.logger.Debugf("pod update (pending): don't know what to do with this pod yet, skipping for now: %+v", obj) + return + } + + p.logger.Debugf("pod update: %+v", obj) + p.emitRunning(pod) +} + +// OnDelete stops pod objects that are deleted +func (p *eventWatcher) OnDelete(obj interface{}) { + p.logger.Debugf("pod delete: %+v", obj) + pod := obj.(*kubernetes.Pod) + time.AfterFunc(p.cleanupTimeout, func() { p.emitStopped(pod) }) +} From 306332f8d6bb924c9b9fee834079c788cd6ec2eb Mon Sep 17 00:00:00 2001 From: Michal Pristas Date: Tue, 6 Oct 2020 19:20:25 +0200 Subject: [PATCH 40/93] [Ingest Manager] Improved verify experience (#21573) [Ingest Manager] Improved verify experience (#21573) --- .../pkg/agent/application/emitter.go | 13 ++++++++---- .../application/upgrade/step_download.go | 2 +- .../pkg/agent/operation/common_test.go | 2 +- .../pkg/agent/operation/operation_verify.go | 2 +- .../artifact/download/composed/verifier.go | 7 ++++--- .../pkg/artifact/download/fs/verifier.go | 17 +++++++++------- .../pkg/artifact/download/fs/verifier_test.go | 6 +++--- .../artifact/download/http/elastic_test.go | 2 +- .../pkg/artifact/download/http/verifier.go | 20 ++++++++++--------- .../pkg/artifact/download/verifier.go | 2 +- .../pkg/core/plugin/process/app.go | 2 +- .../pkg/core/plugin/process/start.go | 2 +- 12 files changed, 44 insertions(+), 33 deletions(-) diff --git a/x-pack/elastic-agent/pkg/agent/application/emitter.go b/x-pack/elastic-agent/pkg/agent/application/emitter.go index fc103366826..3bd06043c30 100644 --- a/x-pack/elastic-agent/pkg/agent/application/emitter.go +++ b/x-pack/elastic-agent/pkg/agent/application/emitter.go @@ -44,10 +44,11 @@ type emitterController struct { reloadables []reloadable // state - lock sync.RWMutex - config *config.Config - ast *transpiler.AST - vars []*transpiler.Vars + lock sync.RWMutex + updateLock sync.Mutex + config *config.Config + ast *transpiler.AST + vars []*transpiler.Vars } func (e *emitterController) Update(c *config.Config) error { @@ -93,6 +94,10 @@ func (e *emitterController) Set(vars []*transpiler.Vars) { } func (e *emitterController) update() error { + // locking whole update because it can be called concurrently via Set and Update method + e.updateLock.Lock() + defer e.updateLock.Unlock() + e.lock.RLock() cfg := e.config rawAst := e.ast diff --git a/x-pack/elastic-agent/pkg/agent/application/upgrade/step_download.go b/x-pack/elastic-agent/pkg/agent/application/upgrade/step_download.go index cf3a3656724..3aea96da0ab 100644 --- a/x-pack/elastic-agent/pkg/agent/application/upgrade/step_download.go +++ b/x-pack/elastic-agent/pkg/agent/application/upgrade/step_download.go @@ -38,7 +38,7 @@ func (u *Upgrader) downloadArtifact(ctx context.Context, version, sourceURI stri return "", errors.New(err, "failed upgrade of agent binary") } - matches, err := verifier.Verify(agentName, version, agentArtifactName) + matches, err := verifier.Verify(agentName, version, agentArtifactName, true) if err != nil { return "", errors.New(err, "failed verification of agent binary") } diff --git a/x-pack/elastic-agent/pkg/agent/operation/common_test.go b/x-pack/elastic-agent/pkg/agent/operation/common_test.go index 6e9b042fe92..e9d40bece87 100644 --- a/x-pack/elastic-agent/pkg/agent/operation/common_test.go +++ b/x-pack/elastic-agent/pkg/agent/operation/common_test.go @@ -143,7 +143,7 @@ var _ download.Downloader = &DummyDownloader{} type DummyVerifier struct{} -func (*DummyVerifier) Verify(p, v, _ string) (bool, error) { +func (*DummyVerifier) Verify(p, v, _ string, _ bool) (bool, error) { return true, nil } diff --git a/x-pack/elastic-agent/pkg/agent/operation/operation_verify.go b/x-pack/elastic-agent/pkg/agent/operation/operation_verify.go index 97cf906cace..7626d339e57 100644 --- a/x-pack/elastic-agent/pkg/agent/operation/operation_verify.go +++ b/x-pack/elastic-agent/pkg/agent/operation/operation_verify.go @@ -66,7 +66,7 @@ func (o *operationVerify) Run(_ context.Context, application Application) (err e } }() - isVerified, err := o.verifier.Verify(o.program.BinaryName(), o.program.Version(), o.program.ArtifactName()) + isVerified, err := o.verifier.Verify(o.program.BinaryName(), o.program.Version(), o.program.ArtifactName(), true) if err != nil { return errors.New(err, fmt.Sprintf("operation '%s' failed to verify %s.%s", o.Name(), o.program.BinaryName(), o.program.Version()), diff --git a/x-pack/elastic-agent/pkg/artifact/download/composed/verifier.go b/x-pack/elastic-agent/pkg/artifact/download/composed/verifier.go index 9d6c4477733..663484130f4 100644 --- a/x-pack/elastic-agent/pkg/artifact/download/composed/verifier.go +++ b/x-pack/elastic-agent/pkg/artifact/download/composed/verifier.go @@ -29,11 +29,12 @@ func NewVerifier(verifiers ...download.Verifier) *Verifier { } // Verify checks the package from configured source. -func (e *Verifier) Verify(programName, version, artifactName string) (bool, error) { +func (e *Verifier) Verify(programName, version, artifactName string, removeOnFailure bool) (bool, error) { var err error - for _, v := range e.vv { - b, e := v.Verify(programName, version, artifactName) + for i, v := range e.vv { + isLast := (i + 1) == len(e.vv) + b, e := v.Verify(programName, version, artifactName, isLast && removeOnFailure) if e == nil { return b, nil } diff --git a/x-pack/elastic-agent/pkg/artifact/download/fs/verifier.go b/x-pack/elastic-agent/pkg/artifact/download/fs/verifier.go index 09462ef3f23..56652d4f69c 100644 --- a/x-pack/elastic-agent/pkg/artifact/download/fs/verifier.go +++ b/x-pack/elastic-agent/pkg/artifact/download/fs/verifier.go @@ -51,20 +51,23 @@ func NewVerifier(config *artifact.Config, allowEmptyPgp bool, pgp []byte) (*Veri // Verify checks downloaded package on preconfigured // location agains a key stored on elastic.co website. -func (v *Verifier) Verify(programName, version, artifactName string) (bool, error) { +func (v *Verifier) Verify(programName, version, artifactName string, removeOnFailure bool) (isMatch bool, err error) { filename, err := artifact.GetArtifactName(programName, version, v.config.OS(), v.config.Arch()) if err != nil { return false, errors.New(err, "retrieving package name") } fullPath := filepath.Join(v.config.TargetDirectory, filename) + defer func() { + if removeOnFailure && (!isMatch || err != nil) { + // remove bits so they can be redownloaded + os.Remove(fullPath) + os.Remove(fullPath + ".sha512") + os.Remove(fullPath + ".asc") + } + }() - isMatch, err := v.verifyHash(filename, fullPath) - if !isMatch || err != nil { - // remove bits so they can be redownloaded - os.Remove(fullPath) - os.Remove(fullPath + ".sha512") - os.Remove(fullPath + ".asc") + if isMatch, err := v.verifyHash(filename, fullPath); !isMatch || err != nil { return isMatch, err } diff --git a/x-pack/elastic-agent/pkg/artifact/download/fs/verifier_test.go b/x-pack/elastic-agent/pkg/artifact/download/fs/verifier_test.go index 975d9ecb14d..a79f35bdd6f 100644 --- a/x-pack/elastic-agent/pkg/artifact/download/fs/verifier_test.go +++ b/x-pack/elastic-agent/pkg/artifact/download/fs/verifier_test.go @@ -65,7 +65,7 @@ func TestFetchVerify(t *testing.T) { // first download verify should fail: // download skipped, as invalid package is prepared upfront // verify fails and cleans download - matches, err := verifier.Verify(programName, version, artifactName) + matches, err := verifier.Verify(programName, version, artifactName, true) assert.NoError(t, err) assert.Equal(t, false, matches) @@ -88,7 +88,7 @@ func TestFetchVerify(t *testing.T) { _, err = os.Stat(hashTargetFilePath) assert.NoError(t, err) - matches, err = verifier.Verify(programName, version, artifactName) + matches, err = verifier.Verify(programName, version, artifactName, true) assert.NoError(t, err) assert.Equal(t, true, matches) } @@ -162,7 +162,7 @@ func TestVerify(t *testing.T) { t.Fatal(err) } - isOk, err := testVerifier.Verify(beatName, version, artifactName) + isOk, err := testVerifier.Verify(beatName, version, artifactName, true) if err != nil { t.Fatal(err) } diff --git a/x-pack/elastic-agent/pkg/artifact/download/http/elastic_test.go b/x-pack/elastic-agent/pkg/artifact/download/http/elastic_test.go index fec1d991c88..0ff4dfd2b93 100644 --- a/x-pack/elastic-agent/pkg/artifact/download/http/elastic_test.go +++ b/x-pack/elastic-agent/pkg/artifact/download/http/elastic_test.go @@ -110,7 +110,7 @@ func TestVerify(t *testing.T) { t.Fatal(err) } - isOk, err := testVerifier.Verify(beatName, version, artifactName) + isOk, err := testVerifier.Verify(beatName, version, artifactName, false) if err != nil { t.Fatal(err) } diff --git a/x-pack/elastic-agent/pkg/artifact/download/http/verifier.go b/x-pack/elastic-agent/pkg/artifact/download/http/verifier.go index c0dfef9b30e..b5b5e628b4a 100644 --- a/x-pack/elastic-agent/pkg/artifact/download/http/verifier.go +++ b/x-pack/elastic-agent/pkg/artifact/download/http/verifier.go @@ -59,9 +59,7 @@ func NewVerifier(config *artifact.Config, allowEmptyPgp bool, pgp []byte) (*Veri // Verify checks downloaded package on preconfigured // location agains a key stored on elastic.co website. -func (v *Verifier) Verify(programName, version, artifactName string) (bool, error) { - // TODO: think about verifying asc for prepacked beats - +func (v *Verifier) Verify(programName, version, artifactName string, removeOnFailure bool) (isMatch bool, err error) { filename, err := artifact.GetArtifactName(programName, version, v.config.OS(), v.config.Arch()) if err != nil { return false, errors.New(err, "retrieving package name") @@ -72,12 +70,16 @@ func (v *Verifier) Verify(programName, version, artifactName string) (bool, erro return false, errors.New(err, "retrieving package path") } - isMatch, err := v.verifyHash(filename, fullPath) - if !isMatch || err != nil { - // remove bits so they can be redownloaded - os.Remove(fullPath) - os.Remove(fullPath + ".sha512") - os.Remove(fullPath + ".asc") + defer func() { + if removeOnFailure && (!isMatch || err != nil) { + // remove bits so they can be redownloaded + os.Remove(fullPath) + os.Remove(fullPath + ".sha512") + os.Remove(fullPath + ".asc") + } + }() + + if isMatch, err := v.verifyHash(filename, fullPath); !isMatch || err != nil { return isMatch, err } diff --git a/x-pack/elastic-agent/pkg/artifact/download/verifier.go b/x-pack/elastic-agent/pkg/artifact/download/verifier.go index 491979514ea..d7b2bc7a415 100644 --- a/x-pack/elastic-agent/pkg/artifact/download/verifier.go +++ b/x-pack/elastic-agent/pkg/artifact/download/verifier.go @@ -6,5 +6,5 @@ package download // Verifier is an interface verifying GPG key of a downloaded artifact type Verifier interface { - Verify(programName, version, artifactName string) (bool, error) + Verify(programName, version, artifactName string, removeOnFailure bool) (bool, error) } diff --git a/x-pack/elastic-agent/pkg/core/plugin/process/app.go b/x-pack/elastic-agent/pkg/core/plugin/process/app.go index 5f9cf6efb25..8732af24f68 100644 --- a/x-pack/elastic-agent/pkg/core/plugin/process/app.go +++ b/x-pack/elastic-agent/pkg/core/plugin/process/app.go @@ -117,7 +117,7 @@ func (a *Application) Name() string { // Started returns true if the application is started. func (a *Application) Started() bool { - return a.state.Status != state.Stopped + return a.state.Status != state.Stopped && a.state.Status != state.Crashed && a.state.Status != state.Failed } // Stop stops the current application. diff --git a/x-pack/elastic-agent/pkg/core/plugin/process/start.go b/x-pack/elastic-agent/pkg/core/plugin/process/start.go index c5f1ffb3842..bc19910e53c 100644 --- a/x-pack/elastic-agent/pkg/core/plugin/process/start.go +++ b/x-pack/elastic-agent/pkg/core/plugin/process/start.go @@ -39,7 +39,7 @@ func (a *Application) start(ctx context.Context, t app.Taggable, cfg map[string] }() // already started if not stopped or crashed - if a.state.Status != state.Stopped && a.state.Status != state.Crashed && a.state.Status != state.Failed { + if a.Started() { return nil } From 0b9213c5d27d2b1e6141b00285bda419c82dcd41 Mon Sep 17 00:00:00 2001 From: Michal Pristas Date: Tue, 6 Oct 2020 19:25:54 +0200 Subject: [PATCH 41/93] [Ingest Manager] Use new form of fleet API paths (#21478) [Ingest Manager] Use new form of fleet API paths (#21478) --- .../docker/docker-entrypoint.elastic-agent.tmpl | 8 ++++---- x-pack/elastic-agent/CHANGELOG.next.asciidoc | 1 + .../pkg/agent/application/enroll_cmd_test.go | 10 +++++----- x-pack/elastic-agent/pkg/fleetapi/ack_cmd.go | 2 +- x-pack/elastic-agent/pkg/fleetapi/ack_cmd_test.go | 2 +- x-pack/elastic-agent/pkg/fleetapi/checkin_cmd.go | 2 +- .../elastic-agent/pkg/fleetapi/checkin_cmd_test.go | 12 ++++++------ x-pack/elastic-agent/pkg/fleetapi/enroll_cmd.go | 4 ++-- x-pack/elastic-agent/pkg/fleetapi/enroll_cmd_test.go | 4 ++-- 9 files changed, 23 insertions(+), 22 deletions(-) diff --git a/dev-tools/packaging/templates/docker/docker-entrypoint.elastic-agent.tmpl b/dev-tools/packaging/templates/docker/docker-entrypoint.elastic-agent.tmpl index 91f043d2799..d4c8d6e4645 100644 --- a/dev-tools/packaging/templates/docker/docker-entrypoint.elastic-agent.tmpl +++ b/dev-tools/packaging/templates/docker/docker-entrypoint.elastic-agent.tmpl @@ -13,8 +13,8 @@ set -eo pipefail # KIBANA_USERNAME - username for accessing kibana API [elastic] function setup(){ - curl -X POST ${KIBANA_HOST:-http://localhost:5601}/api/ingest_manager/setup -H 'kbn-xsrf: true' -u ${KIBANA_USERNAME:-elastic}:${KIBANA_PASSWORD:-changeme} - curl -X POST ${KIBANA_HOST:-http://localhost:5601}/api/ingest_manager/fleet/setup \ + curl -X POST ${KIBANA_HOST:-http://localhost:5601}/api/fleet/setup -H 'kbn-xsrf: true' -u ${KIBANA_USERNAME:-elastic}:${KIBANA_PASSWORD:-changeme} + curl -X POST ${KIBANA_HOST:-http://localhost:5601}/api/fleet/agents/setup \ -H 'Content-Type: application/json' \ -H 'kbn-xsrf: true' \ -u ${KIBANA_USERNAME:-elastic}:${KIBANA_PASSWORD:-changeme} @@ -27,7 +27,7 @@ function enroll(){ if [[ -n "${FLEET_ENROLLMENT_TOKEN}" ]]; then apikey="${FLEET_ENROLLMENT_TOKEN}" else - enrollResp=$(curl ${KIBANA_HOST:-http://localhost:5601}/api/ingest_manager/fleet/enrollment-api-keys \ + enrollResp=$(curl ${KIBANA_HOST:-http://localhost:5601}/api/fleet/enrollment-api-keys \ -H 'Content-Type: application/json' \ -H 'kbn-xsrf: true' \ -u ${KIBANA_USERNAME:-elastic}:${KIBANA_PASSWORD:-changeme} ) @@ -40,7 +40,7 @@ function enroll(){ local apikeyId=$(echo $enrollResp | jq -r '.list[0].id') echo $apikeyId - enrollResp=$(curl ${KIBANA_HOST:-http://localhost:5601}/api/ingest_manager/fleet/enrollment-api-keys/$apikeyId \ + enrollResp=$(curl ${KIBANA_HOST:-http://localhost:5601}/api/fleet/enrollment-api-keys/$apikeyId \ -H 'Content-Type: application/json' \ -H 'kbn-xsrf: true' \ -u ${KIBANA_USERNAME:-elastic}:${KIBANA_PASSWORD:-changeme} ) diff --git a/x-pack/elastic-agent/CHANGELOG.next.asciidoc b/x-pack/elastic-agent/CHANGELOG.next.asciidoc index 897b64c517e..a0cd6f6291e 100644 --- a/x-pack/elastic-agent/CHANGELOG.next.asciidoc +++ b/x-pack/elastic-agent/CHANGELOG.next.asciidoc @@ -28,6 +28,7 @@ - Add support for EQL based condition on inputs {pull}20994[20994] - Send `fleet.host.id` to Endpoint Security {pull}21042[21042] - Add `install` and `uninstall` subcommands {pull}21206[21206] +- Use new form of fleet API paths {pull}21478[21478] - Add `kubernetes` composable dynamic provider. {pull}21480[21480] - Send updating state {pull}21461[21461] - Add `elastic.agent.id` and `elastic.agent.version` to published events from filebeat and metricbeat {pull}21543[21543] diff --git a/x-pack/elastic-agent/pkg/agent/application/enroll_cmd_test.go b/x-pack/elastic-agent/pkg/agent/application/enroll_cmd_test.go index beab1b253d6..080b5efcb69 100644 --- a/x-pack/elastic-agent/pkg/agent/application/enroll_cmd_test.go +++ b/x-pack/elastic-agent/pkg/agent/application/enroll_cmd_test.go @@ -49,7 +49,7 @@ func TestEnroll(t *testing.T) { t.Run("fail to save is propagated", withTLSServer( func(t *testing.T) *http.ServeMux { mux := http.NewServeMux() - mux.HandleFunc("/api/ingest_manager/fleet/agents/enroll", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/api/fleet/agents/enroll", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte(` { @@ -102,7 +102,7 @@ func TestEnroll(t *testing.T) { t.Run("successfully enroll with TLS and save access api key in the store", withTLSServer( func(t *testing.T) *http.ServeMux { mux := http.NewServeMux() - mux.HandleFunc("/api/ingest_manager/fleet/agents/enroll", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/api/fleet/agents/enroll", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte(` { @@ -163,7 +163,7 @@ func TestEnroll(t *testing.T) { t.Run("successfully enroll when a slash is defined at the end of host", withServer( func(t *testing.T) *http.ServeMux { mux := http.NewServeMux() - mux.HandleFunc("/api/ingest_manager/fleet/agents/enroll", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/api/fleet/agents/enroll", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte(` { @@ -223,7 +223,7 @@ func TestEnroll(t *testing.T) { t.Run("successfully enroll without TLS and save access api key in the store", withServer( func(t *testing.T) *http.ServeMux { mux := http.NewServeMux() - mux.HandleFunc("/api/ingest_manager/fleet/agents/enroll", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/api/fleet/agents/enroll", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte(` { @@ -283,7 +283,7 @@ func TestEnroll(t *testing.T) { t.Run("fail to enroll without TLS", withServer( func(t *testing.T) *http.ServeMux { mux := http.NewServeMux() - mux.HandleFunc("/api/ingest_manager/fleet/agents/enroll", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/api/fleet/agents/enroll", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(` { diff --git a/x-pack/elastic-agent/pkg/fleetapi/ack_cmd.go b/x-pack/elastic-agent/pkg/fleetapi/ack_cmd.go index ac568c884d1..5c197289ed9 100644 --- a/x-pack/elastic-agent/pkg/fleetapi/ack_cmd.go +++ b/x-pack/elastic-agent/pkg/fleetapi/ack_cmd.go @@ -14,7 +14,7 @@ import ( "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" ) -const ackPath = "/api/ingest_manager/fleet/agents/%s/acks" +const ackPath = "/api/fleet/agents/%s/acks" // AckEvent is an event sent in an ACK request. type AckEvent struct { diff --git a/x-pack/elastic-agent/pkg/fleetapi/ack_cmd_test.go b/x-pack/elastic-agent/pkg/fleetapi/ack_cmd_test.go index 75940e928b7..1f1bfdb21eb 100644 --- a/x-pack/elastic-agent/pkg/fleetapi/ack_cmd_test.go +++ b/x-pack/elastic-agent/pkg/fleetapi/ack_cmd_test.go @@ -22,7 +22,7 @@ func TestAck(t *testing.T) { func(t *testing.T) *http.ServeMux { raw := `{"action": "ack"}` mux := http.NewServeMux() - path := fmt.Sprintf("/api/ingest_manager/fleet/agents/%s/acks", agentInfo.AgentID()) + path := fmt.Sprintf("/api/fleet/agents/%s/acks", agentInfo.AgentID()) mux.HandleFunc(path, authHandler(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) diff --git a/x-pack/elastic-agent/pkg/fleetapi/checkin_cmd.go b/x-pack/elastic-agent/pkg/fleetapi/checkin_cmd.go index 58c80b0adf1..9758fd4236c 100644 --- a/x-pack/elastic-agent/pkg/fleetapi/checkin_cmd.go +++ b/x-pack/elastic-agent/pkg/fleetapi/checkin_cmd.go @@ -17,7 +17,7 @@ import ( "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" ) -const checkingPath = "/api/ingest_manager/fleet/agents/%s/checkin" +const checkingPath = "/api/fleet/agents/%s/checkin" // CheckinRequest consists of multiple events reported to fleet ui. type CheckinRequest struct { diff --git a/x-pack/elastic-agent/pkg/fleetapi/checkin_cmd_test.go b/x-pack/elastic-agent/pkg/fleetapi/checkin_cmd_test.go index af8b4da81ea..3e88ed29cd1 100644 --- a/x-pack/elastic-agent/pkg/fleetapi/checkin_cmd_test.go +++ b/x-pack/elastic-agent/pkg/fleetapi/checkin_cmd_test.go @@ -34,7 +34,7 @@ func TestCheckin(t *testing.T) { } ` mux := http.NewServeMux() - path := fmt.Sprintf("/api/ingest_manager/fleet/agents/%s/checkin", agentInfo.AgentID()) + path := fmt.Sprintf("/api/fleet/agents/%s/checkin", agentInfo.AgentID()) mux.HandleFunc(path, authHandler(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, raw) @@ -83,7 +83,7 @@ func TestCheckin(t *testing.T) { } ` mux := http.NewServeMux() - path := fmt.Sprintf("/api/ingest_manager/fleet/agents/%s/checkin", agentInfo.AgentID()) + path := fmt.Sprintf("/api/fleet/agents/%s/checkin", agentInfo.AgentID()) mux.HandleFunc(path, authHandler(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) fmt.Fprintf(w, raw) @@ -144,7 +144,7 @@ func TestCheckin(t *testing.T) { } ` mux := http.NewServeMux() - path := fmt.Sprintf("/api/ingest_manager/fleet/agents/%s/checkin", agentInfo.AgentID()) + path := fmt.Sprintf("/api/fleet/agents/%s/checkin", agentInfo.AgentID()) mux.HandleFunc(path, authHandler(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) fmt.Fprintf(w, raw) @@ -176,7 +176,7 @@ func TestCheckin(t *testing.T) { func(t *testing.T) *http.ServeMux { raw := `{ "actions": [] }` mux := http.NewServeMux() - path := fmt.Sprintf("/api/ingest_manager/fleet/agents/%s/checkin", agentInfo.AgentID()) + path := fmt.Sprintf("/api/fleet/agents/%s/checkin", agentInfo.AgentID()) mux.HandleFunc(path, authHandler(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) fmt.Fprintf(w, raw) @@ -199,7 +199,7 @@ func TestCheckin(t *testing.T) { func(t *testing.T) *http.ServeMux { raw := `{"actions": []}` mux := http.NewServeMux() - path := fmt.Sprintf("/api/ingest_manager/fleet/agents/%s/checkin", agentInfo.AgentID()) + path := fmt.Sprintf("/api/fleet/agents/%s/checkin", agentInfo.AgentID()) mux.HandleFunc(path, authHandler(func(w http.ResponseWriter, r *http.Request) { type Request struct { Metadata *info.ECSMeta `json:"local_metadata"` @@ -233,7 +233,7 @@ func TestCheckin(t *testing.T) { func(t *testing.T) *http.ServeMux { raw := `{"actions": []}` mux := http.NewServeMux() - path := fmt.Sprintf("/api/ingest_manager/fleet/agents/%s/checkin", agentInfo.AgentID()) + path := fmt.Sprintf("/api/fleet/agents/%s/checkin", agentInfo.AgentID()) mux.HandleFunc(path, authHandler(func(w http.ResponseWriter, r *http.Request) { type Request struct { Metadata *info.ECSMeta `json:"local_metadata"` diff --git a/x-pack/elastic-agent/pkg/fleetapi/enroll_cmd.go b/x-pack/elastic-agent/pkg/fleetapi/enroll_cmd.go index 3e6336fbc81..29168be0ae4 100644 --- a/x-pack/elastic-agent/pkg/fleetapi/enroll_cmd.go +++ b/x-pack/elastic-agent/pkg/fleetapi/enroll_cmd.go @@ -71,7 +71,7 @@ func (p EnrollType) MarshalJSON() ([]byte, error) { // EnrollRequest is the data required to enroll the elastic-agent into Fleet. // // Example: -// POST /api/ingest_manager/fleet/agents/enroll +// POST /api/fleet/agents/enroll // { // "type": "PERMANENT", // "metadata": { @@ -168,7 +168,7 @@ type EnrollCmd struct { // Execute enroll the Agent in the Fleet. func (e *EnrollCmd) Execute(ctx context.Context, r *EnrollRequest) (*EnrollResponse, error) { - const p = "/api/ingest_manager/fleet/agents/enroll" + const p = "/api/fleet/agents/enroll" const key = "Authorization" const prefix = "ApiKey " diff --git a/x-pack/elastic-agent/pkg/fleetapi/enroll_cmd_test.go b/x-pack/elastic-agent/pkg/fleetapi/enroll_cmd_test.go index 6533ad6643e..29bbf7e607e 100644 --- a/x-pack/elastic-agent/pkg/fleetapi/enroll_cmd_test.go +++ b/x-pack/elastic-agent/pkg/fleetapi/enroll_cmd_test.go @@ -23,7 +23,7 @@ func TestEnroll(t *testing.T) { t.Run("Successful enroll", withServer( func(t *testing.T) *http.ServeMux { mux := http.NewServeMux() - mux.HandleFunc("/api/ingest_manager/fleet/agents/enroll", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/api/fleet/agents/enroll", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") @@ -92,7 +92,7 @@ func TestEnroll(t *testing.T) { t.Run("Raise back any server errors", withServer( func(t *testing.T) *http.ServeMux { mux := http.NewServeMux() - mux.HandleFunc("/api/ingest_manager/fleet/agents/enroll", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/api/fleet/agents/enroll", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) w.Header().Set("Content-Type", "application/json") w.Write([]byte(`{"statusCode": 500, "error":"Something is really bad here"}`)) From 31cf0acee9666e492d4e98f5aafef6b9989e2a6e Mon Sep 17 00:00:00 2001 From: Marius Iversen Date: Tue, 6 Oct 2020 20:30:22 +0200 Subject: [PATCH 42/93] [Filebeat][S3 Input] Add support for FIPS endpoints (#21585) * adding fips support for s3 input --- CHANGELOG.next.asciidoc | 1 + x-pack/filebeat/docs/inputs/input-aws-s3.asciidoc | 5 +++++ x-pack/filebeat/input/s3/config.go | 2 ++ x-pack/filebeat/input/s3/input.go | 9 ++++++++- 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 99daa875a00..6d22c4c1bf9 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -612,6 +612,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - New Cisco Umbrella dataset {pull}21504[21504] - New juniper.srx dataset for Juniper SRX logs. {pull}20017[20017] - Adding support for Microsoft 365 Defender (Microsoft Threat Protection) {pull}21446[21446] +- Adding support for FIPS in s3 input {pull}21446[21446] *Heartbeat* diff --git a/x-pack/filebeat/docs/inputs/input-aws-s3.asciidoc b/x-pack/filebeat/docs/inputs/input-aws-s3.asciidoc index 8891e38fcc4..5cbe4685cb8 100644 --- a/x-pack/filebeat/docs/inputs/input-aws-s3.asciidoc +++ b/x-pack/filebeat/docs/inputs/input-aws-s3.asciidoc @@ -42,6 +42,11 @@ The `s3` input supports the following configuration options plus the URL of the AWS SQS queue that messages will be received from. Required. +[float] +==== `fips_enabled` + +Enabling this option changes the service name from `s3` to `s3-fips` for connecting to the correct service endpoint. For example: `s3-fips.us-gov-east-1.amazonaws.com`. + [float] ==== `visibility_timeout` diff --git a/x-pack/filebeat/input/s3/config.go b/x-pack/filebeat/input/s3/config.go index 5f37a436d12..cc3c5318289 100644 --- a/x-pack/filebeat/input/s3/config.go +++ b/x-pack/filebeat/input/s3/config.go @@ -15,6 +15,7 @@ import ( type config struct { QueueURL string `config:"queue_url" validate:"nonzero,required"` VisibilityTimeout time.Duration `config:"visibility_timeout"` + FipsEnabled bool `config:"fips_enabled"` AwsConfig awscommon.ConfigAWS `config:",inline"` ExpandEventListFromField string `config:"expand_event_list_from_field"` APITimeout time.Duration `config:"api_timeout"` @@ -32,6 +33,7 @@ func defaultConfig() config { return config{ VisibilityTimeout: 300 * time.Second, APITimeout: 120 * time.Second, + FipsEnabled: false, } } diff --git a/x-pack/filebeat/input/s3/input.go b/x-pack/filebeat/input/s3/input.go index a6b56d03970..d76e5b8b728 100644 --- a/x-pack/filebeat/input/s3/input.go +++ b/x-pack/filebeat/input/s3/input.go @@ -100,6 +100,13 @@ func (in *s3Input) createCollector(ctx v2.Context, pipeline beat.Pipeline) (*s3C log.Infof("visibility timeout is set to %v seconds", visibilityTimeout) log.Infof("aws api timeout is set to %v", in.config.APITimeout) + s3Servicename := "s3" + if in.config.FipsEnabled { + s3Servicename = "s3-fips" + } + + log.Debug("s3 service name = ", s3Servicename) + return &s3Collector{ cancellation: ctxtool.FromCanceller(ctx.Cancelation), logger: log, @@ -107,7 +114,7 @@ func (in *s3Input) createCollector(ctx v2.Context, pipeline beat.Pipeline) (*s3C publisher: client, visibilityTimeout: visibilityTimeout, sqs: sqs.New(awscommon.EnrichAWSConfigWithEndpoint(in.config.AwsConfig.Endpoint, "sqs", regionName, awsConfig)), - s3: s3.New(awscommon.EnrichAWSConfigWithEndpoint(in.config.AwsConfig.Endpoint, "s3", regionName, awsConfig)), + s3: s3.New(awscommon.EnrichAWSConfigWithEndpoint(in.config.AwsConfig.Endpoint, s3Servicename, regionName, awsConfig)), }, nil } From b4c273c0259c166efc5fbd45903d27a2c1bbe0f2 Mon Sep 17 00:00:00 2001 From: kaiyan-sheng Date: Tue, 6 Oct 2020 12:40:21 -0600 Subject: [PATCH 43/93] [Metricbeat] Use timestamp from CloudWatch for events (#21498) --- CHANGELOG.next.asciidoc | 1 + x-pack/metricbeat/module/aws/aws.go | 13 +- .../metricbeat/module/aws/billing/billing.go | 38 ++-- .../module/aws/cloudwatch/cloudwatch.go | 105 ++++++----- .../module/aws/cloudwatch/cloudwatch_test.go | 39 ++++- x-pack/metricbeat/module/aws/ec2/ec2.go | 164 +++++++++--------- x-pack/metricbeat/module/aws/rds/rds.go | 106 +++++------ .../aws/s3_daily_storage/s3_daily_storage.go | 3 +- .../module/aws/s3_request/s3_request.go | 3 +- x-pack/metricbeat/module/aws/sqs/sqs.go | 7 +- 10 files changed, 255 insertions(+), 224 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 6d22c4c1bf9..3099a033153 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -362,6 +362,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Fix remote_write flaky test. {pull}21173[21173] - Visualization title fixes in aws, azure and googlecloud compute dashboards. {pull}21098[21098] - Add a switch to the driver definition on SQL module to use pretty names {pull}17378[17378] +- Use timestamp from CloudWatch API when creating events. {pull}21498[21498] *Packetbeat* diff --git a/x-pack/metricbeat/module/aws/aws.go b/x-pack/metricbeat/module/aws/aws.go index 9786c4f7b38..167e6a088a0 100644 --- a/x-pack/metricbeat/module/aws/aws.go +++ b/x-pack/metricbeat/module/aws/aws.go @@ -196,11 +196,14 @@ func StringInSlice(str string, list []string) (bool, int) { } // InitEvent initialize mb.Event with basic information like service.name, cloud.provider -func InitEvent(regionName string, accountName string, accountID string) mb.Event { - event := mb.Event{} - event.MetricSetFields = common.MapStr{} - event.ModuleFields = common.MapStr{} - event.RootFields = common.MapStr{} +func InitEvent(regionName string, accountName string, accountID string, timestamp time.Time) mb.Event { + event := mb.Event{ + Timestamp: timestamp, + MetricSetFields: common.MapStr{}, + ModuleFields: common.MapStr{}, + RootFields: common.MapStr{}, + } + event.RootFields.Put("cloud.provider", "aws") if regionName != "" { event.RootFields.Put("cloud.region", regionName) diff --git a/x-pack/metricbeat/module/aws/billing/billing.go b/x-pack/metricbeat/module/aws/billing/billing.go index b9b971e3d34..39d600983d0 100644 --- a/x-pack/metricbeat/module/aws/billing/billing.go +++ b/x-pack/metricbeat/module/aws/billing/billing.go @@ -178,26 +178,28 @@ func (m *MetricSet) getCloudWatchBillingMetrics( // Find a timestamp for all metrics in output timestamp := aws.FindTimestamp(metricDataOutput) - if !timestamp.IsZero() { - for _, output := range metricDataOutput { - if len(output.Values) == 0 { - continue - } - exists, timestampIdx := aws.CheckTimestampInArray(timestamp, output.Timestamps) - if exists { - labels := strings.Split(*output.Label, labelSeparator) + if timestamp.IsZero() { + return nil + } + + for _, output := range metricDataOutput { + if len(output.Values) == 0 { + continue + } + exists, timestampIdx := aws.CheckTimestampInArray(timestamp, output.Timestamps) + if exists { + labels := strings.Split(*output.Label, labelSeparator) - event := aws.InitEvent("", m.AccountName, m.AccountID) - event.MetricSetFields.Put(labels[0], output.Values[timestampIdx]) + event := aws.InitEvent("", m.AccountName, m.AccountID, timestamp) + event.MetricSetFields.Put(labels[0], output.Values[timestampIdx]) - i := 1 - for i < len(labels)-1 { - event.MetricSetFields.Put(labels[i], labels[i+1]) - i += 2 - } - event.Timestamp = endTime - events = append(events, event) + i := 1 + for i < len(labels)-1 { + event.MetricSetFields.Put(labels[i], labels[i+1]) + i += 2 } + event.Timestamp = endTime + events = append(events, event) } } return events @@ -278,7 +280,7 @@ func (m *MetricSet) getCostGroupBy(svcCostExplorer costexploreriface.ClientAPI, } func (m *MetricSet) addCostMetrics(metrics map[string]costexplorer.MetricValue, groupDefinition costexplorer.GroupDefinition, startDate string, endDate string) mb.Event { - event := aws.InitEvent("", m.AccountName, m.AccountID) + event := aws.InitEvent("", m.AccountName, m.AccountID, time.Now()) // add group definition event.MetricSetFields.Put("group_definition", common.MapStr{ diff --git a/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch.go b/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch.go index 07e1f09acef..af04508a111 100644 --- a/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch.go +++ b/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch.go @@ -499,34 +499,35 @@ func (m *MetricSet) createEvents(svcCloudwatch cloudwatchiface.ClientAPI, svcRes // Find a timestamp for all metrics in output timestamp := aws.FindTimestamp(metricDataResults) + if timestamp.IsZero() { + return nil, nil + } // Create events when there is no tags_filter or resource_type specified. if len(resourceTypeTagFilters) == 0 { - if !timestamp.IsZero() { - for _, output := range metricDataResults { - if len(output.Values) == 0 { - continue - } + for _, output := range metricDataResults { + if len(output.Values) == 0 { + continue + } - exists, timestampIdx := aws.CheckTimestampInArray(timestamp, output.Timestamps) - if exists { - labels := strings.Split(*output.Label, labelSeparator) - if len(labels) != 5 { - // when there is no identifier value in label, use region+accountID+namespace instead - identifier := regionName + m.AccountID + labels[namespaceIdx] - if _, ok := events[identifier]; !ok { - events[identifier] = aws.InitEvent(regionName, m.AccountName, m.AccountID) - } - events[identifier] = insertRootFields(events[identifier], output.Values[timestampIdx], labels) - continue + exists, timestampIdx := aws.CheckTimestampInArray(timestamp, output.Timestamps) + if exists { + labels := strings.Split(*output.Label, labelSeparator) + if len(labels) != 5 { + // when there is no identifier value in label, use region+accountID+namespace instead + identifier := regionName + m.AccountID + labels[namespaceIdx] + if _, ok := events[identifier]; !ok { + events[identifier] = aws.InitEvent(regionName, m.AccountName, m.AccountID, timestamp) } + events[identifier] = insertRootFields(events[identifier], output.Values[timestampIdx], labels) + continue + } - identifierValue := labels[identifierValueIdx] - if _, ok := events[identifierValue]; !ok { - events[identifierValue] = aws.InitEvent(regionName, m.AccountName, m.AccountID) - } - events[identifierValue] = insertRootFields(events[identifierValue], output.Values[timestampIdx], labels) + identifierValue := labels[identifierValueIdx] + if _, ok := events[identifierValue]; !ok { + events[identifierValue] = aws.InitEvent(regionName, m.AccountName, m.AccountID, timestamp) } + events[identifierValue] = insertRootFields(events[identifierValue], output.Values[timestampIdx], labels) } } return events, nil @@ -556,45 +557,43 @@ func (m *MetricSet) createEvents(svcCloudwatch cloudwatchiface.ClientAPI, svcRes m.logger.Debugf("In region %s, service %s tags match tags_filter", regionName, identifier) } - if !timestamp.IsZero() { - for _, output := range metricDataResults { - if len(output.Values) == 0 { - continue - } + for _, output := range metricDataResults { + if len(output.Values) == 0 { + continue + } - exists, timestampIdx := aws.CheckTimestampInArray(timestamp, output.Timestamps) - if exists { - labels := strings.Split(*output.Label, labelSeparator) - if len(labels) != 5 { - // if there is no tag in labels but there is a tagsFilter, then no event should be reported. - if len(tagsFilter) != 0 { - continue - } - - // when there is no identifier value in label, use region+accountID+namespace instead - identifier := regionName + m.AccountID + labels[namespaceIdx] - if _, ok := events[identifier]; !ok { - events[identifier] = aws.InitEvent(regionName, m.AccountName, m.AccountID) - } - events[identifier] = insertRootFields(events[identifier], output.Values[timestampIdx], labels) + exists, timestampIdx := aws.CheckTimestampInArray(timestamp, output.Timestamps) + if exists { + labels := strings.Split(*output.Label, labelSeparator) + if len(labels) != 5 { + // if there is no tag in labels but there is a tagsFilter, then no event should be reported. + if len(tagsFilter) != 0 { continue } - identifierValue := labels[identifierValueIdx] - if _, ok := events[identifierValue]; !ok { - // when tagsFilter is not empty but no entry in - // resourceTagMap for this identifier, do not initialize - // an event for this identifier. - if len(tagsFilter) != 0 && resourceTagMap[identifierValue] == nil { - continue - } - events[identifierValue] = aws.InitEvent(regionName, m.AccountName, m.AccountID) + // when there is no identifier value in label, use region+accountID+namespace instead + identifier := regionName + m.AccountID + labels[namespaceIdx] + if _, ok := events[identifier]; !ok { + events[identifier] = aws.InitEvent(regionName, m.AccountName, m.AccountID, timestamp) } - events[identifierValue] = insertRootFields(events[identifierValue], output.Values[timestampIdx], labels) + events[identifier] = insertRootFields(events[identifier], output.Values[timestampIdx], labels) + continue + } - // add tags to event based on identifierValue - insertTags(events, identifierValue, resourceTagMap) + identifierValue := labels[identifierValueIdx] + if _, ok := events[identifierValue]; !ok { + // when tagsFilter is not empty but no entry in + // resourceTagMap for this identifier, do not initialize + // an event for this identifier. + if len(tagsFilter) != 0 && resourceTagMap[identifierValue] == nil { + continue + } + events[identifierValue] = aws.InitEvent(regionName, m.AccountName, m.AccountID, timestamp) } + events[identifierValue] = insertRootFields(events[identifierValue], output.Values[timestampIdx], labels) + + // add tags to event based on identifierValue + insertTags(events, identifierValue, resourceTagMap) } } } diff --git a/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch_test.go b/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch_test.go index 393ddecb07e..ecd4bb2f9d1 100644 --- a/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch_test.go +++ b/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch_test.go @@ -11,9 +11,6 @@ import ( "testing" "time" - "github.com/elastic/beats/v7/libbeat/logp" - "github.com/elastic/beats/v7/metricbeat/mb" - awssdk "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/cloudwatch" "github.com/aws/aws-sdk-go-v2/service/cloudwatch/cloudwatchiface" @@ -22,12 +19,14 @@ import ( "github.com/pkg/errors" "github.com/stretchr/testify/assert" + "github.com/elastic/beats/v7/libbeat/logp" + "github.com/elastic/beats/v7/metricbeat/mb" "github.com/elastic/beats/v7/x-pack/metricbeat/module/aws" ) var ( regionName = "us-west-1" - timestamp = time.Now() + timestamp = time.Date(2020, 10, 06, 00, 00, 00, 0, time.UTC) accountID = "123456789012" accountName = "test" @@ -1466,9 +1465,9 @@ func TestInsertTags(t *testing.T) { tagValue3 := "dev" events := map[string]mb.Event{} - events[identifier1] = aws.InitEvent(regionName, accountName, accountID) - events[identifier2] = aws.InitEvent(regionName, accountName, accountID) - events[identifierContainsArn] = aws.InitEvent(regionName, accountName, accountID) + events[identifier1] = aws.InitEvent(regionName, accountName, accountID, timestamp) + events[identifier2] = aws.InitEvent(regionName, accountName, accountID, timestamp) + events[identifierContainsArn] = aws.InitEvent(regionName, accountName, accountID, timestamp) resourceTagMap := map[string][]resourcegroupstaggingapi.Tag{} resourceTagMap["test-s3-1"] = []resourcegroupstaggingapi.Tag{ @@ -1569,3 +1568,29 @@ func TestConfigDimensionValueContainsWildcard(t *testing.T) { }) } } + +func TestCreateEventsTimestamp(t *testing.T) { + m := MetricSet{ + logger: logp.NewLogger("test"), + CloudwatchConfigs: []Config{{Statistic: []string{"Average"}}}, + MetricSet: &aws.MetricSet{Period: 5, AccountID: accountID}, + } + + listMetricWithStatsTotal := []metricsWithStatistics{ + { + cloudwatch.Metric{ + MetricName: awssdk.String("CPUUtilization"), + Namespace: awssdk.String("AWS/EC2"), + }, + []string{"Average"}, + nil, + }, + } + + resourceTypeTagFilters := map[string][]aws.Tag{} + startTime, endTime := aws.GetStartTimeEndTime(m.MetricSet.Period, m.MetricSet.Latency) + + events, err := m.createEvents(&MockCloudWatchClientWithoutDim{}, &MockResourceGroupsTaggingClient{}, listMetricWithStatsTotal, resourceTypeTagFilters, regionName, startTime, endTime) + assert.NoError(t, err) + assert.Equal(t, timestamp, events[regionName+accountID+namespace].Timestamp) +} diff --git a/x-pack/metricbeat/module/aws/ec2/ec2.go b/x-pack/metricbeat/module/aws/ec2/ec2.go index 4e0776072e6..7cb69623a00 100644 --- a/x-pack/metricbeat/module/aws/ec2/ec2.go +++ b/x-pack/metricbeat/module/aws/ec2/ec2.go @@ -175,115 +175,117 @@ func constructMetricQueries(listMetricsOutput []cloudwatch.Metric, instanceID st } func (m *MetricSet) createCloudWatchEvents(getMetricDataResults []cloudwatch.MetricDataResult, instanceOutput map[string]ec2.Instance, regionName string) (map[string]mb.Event, error) { + // monitoring state for each instance + monitoringStates := map[string]string{} + + // Find a timestamp for all metrics in output + timestamp := aws.FindTimestamp(getMetricDataResults) + if timestamp.IsZero() { + return nil, nil + } + // Initialize events and metricSetFieldResults per instanceID events := map[string]mb.Event{} metricSetFieldResults := map[idStat]map[string]interface{}{} for instanceID := range instanceOutput { for _, statistic := range statistics { - events[instanceID] = aws.InitEvent(regionName, m.AccountName, m.AccountID) + events[instanceID] = aws.InitEvent(regionName, m.AccountName, m.AccountID, timestamp) metricSetFieldResults[idStat{instanceID: instanceID, statistic: statistic}] = map[string]interface{}{} } } - // monitoring state for each instance - monitoringStates := map[string]string{} + for _, output := range getMetricDataResults { + if len(output.Values) == 0 { + continue + } - // Find a timestamp for all metrics in output - timestamp := aws.FindTimestamp(getMetricDataResults) - if !timestamp.IsZero() { - for _, output := range getMetricDataResults { - if len(output.Values) == 0 { + exists, timestampIdx := aws.CheckTimestampInArray(timestamp, output.Timestamps) + if exists { + label, err := newLabelFromJSON(*output.Label) + if err != nil { + m.logger.Errorf("convert cloudwatch MetricDataResult label failed for label = %s: %w", *output.Label, err) continue } - exists, timestampIdx := aws.CheckTimestampInArray(timestamp, output.Timestamps) - if exists { - label, err := newLabelFromJSON(*output.Label) - if err != nil { - m.logger.Errorf("convert cloudwatch MetricDataResult label failed for label = %s: %w", *output.Label, err) + instanceID := label.InstanceID + statistic := label.Statistic + + // Add tags + tags := instanceOutput[instanceID].Tags + if m.TagsFilter != nil { + // Check with each tag filter + // If tag filter doesn't exist in tagKeys/tagValues, + // then do not report this event/instance. + if exists := aws.CheckTagFiltersExist(m.TagsFilter, tags); !exists { + // if tag filter doesn't exist, remove this event initial + // entry to avoid report an empty event. + delete(events, instanceID) continue } + } - instanceID := label.InstanceID - statistic := label.Statistic - - // Add tags - tags := instanceOutput[instanceID].Tags - if m.TagsFilter != nil { - // Check with each tag filter - // If tag filter doesn't exist in tagKeys/tagValues, - // then do not report this event/instance. - if exists := aws.CheckTagFiltersExist(m.TagsFilter, tags); !exists { - // if tag filter doesn't exist, remove this event initial - // entry to avoid report an empty event. - delete(events, instanceID) - continue - } - } - - // By default, replace dot "." using underscore "_" for tag keys. - // Note: tag values are not dedotted. - for _, tag := range tags { - events[instanceID].ModuleFields.Put("tags."+common.DeDot(*tag.Key), *tag.Value) - // add cloud.instance.name and host.name into ec2 events - if *tag.Key == "Name" { - events[instanceID].RootFields.Put("cloud.instance.name", *tag.Value) - events[instanceID].RootFields.Put("host.name", *tag.Value) - } - } - - machineType, err := instanceOutput[instanceID].InstanceType.MarshalValue() - if err != nil { - return events, errors.Wrap(err, "instance.InstanceType.MarshalValue failed") + // By default, replace dot "." using underscore "_" for tag keys. + // Note: tag values are not dedotted. + for _, tag := range tags { + events[instanceID].ModuleFields.Put("tags."+common.DeDot(*tag.Key), *tag.Value) + // add cloud.instance.name and host.name into ec2 events + if *tag.Key == "Name" { + events[instanceID].RootFields.Put("cloud.instance.name", *tag.Value) + events[instanceID].RootFields.Put("host.name", *tag.Value) } + } - events[instanceID].RootFields.Put("cloud.instance.id", instanceID) - events[instanceID].RootFields.Put("cloud.machine.type", machineType) + machineType, err := instanceOutput[instanceID].InstanceType.MarshalValue() + if err != nil { + return events, errors.Wrap(err, "instance.InstanceType.MarshalValue failed") + } - placement := instanceOutput[instanceID].Placement - if placement != nil { - events[instanceID].RootFields.Put("cloud.availability_zone", *placement.AvailabilityZone) - } + events[instanceID].RootFields.Put("cloud.instance.id", instanceID) + events[instanceID].RootFields.Put("cloud.machine.type", machineType) - if len(output.Values) > timestampIdx { - metricSetFieldResults[idStat{instanceID: instanceID, statistic: statistic}][label.MetricName] = fmt.Sprint(output.Values[timestampIdx]) - } + placement := instanceOutput[instanceID].Placement + if placement != nil { + events[instanceID].RootFields.Put("cloud.availability_zone", *placement.AvailabilityZone) + } - instanceStateName, err := instanceOutput[instanceID].State.Name.MarshalValue() - if err != nil { - return events, errors.Wrap(err, "instance.State.Name.MarshalValue failed") - } + if len(output.Values) > timestampIdx { + metricSetFieldResults[idStat{instanceID: instanceID, statistic: statistic}][label.MetricName] = fmt.Sprint(output.Values[timestampIdx]) + } - monitoringState, err := instanceOutput[instanceID].Monitoring.State.MarshalValue() - if err != nil { - return events, errors.Wrap(err, "instance.Monitoring.State.MarshalValue failed") - } + instanceStateName, err := instanceOutput[instanceID].State.Name.MarshalValue() + if err != nil { + return events, errors.Wrap(err, "instance.State.Name.MarshalValue failed") + } - monitoringStates[instanceID] = monitoringState + monitoringState, err := instanceOutput[instanceID].Monitoring.State.MarshalValue() + if err != nil { + return events, errors.Wrap(err, "instance.Monitoring.State.MarshalValue failed") + } - cpuOptions := instanceOutput[instanceID].CpuOptions - if cpuOptions != nil { - events[instanceID].MetricSetFields.Put("instance.core.count", *cpuOptions.CoreCount) - events[instanceID].MetricSetFields.Put("instance.threads_per_core", *cpuOptions.ThreadsPerCore) - } + monitoringStates[instanceID] = monitoringState - publicIP := instanceOutput[instanceID].PublicIpAddress - if publicIP != nil { - events[instanceID].MetricSetFields.Put("instance.public.ip", *publicIP) - } + cpuOptions := instanceOutput[instanceID].CpuOptions + if cpuOptions != nil { + events[instanceID].MetricSetFields.Put("instance.core.count", *cpuOptions.CoreCount) + events[instanceID].MetricSetFields.Put("instance.threads_per_core", *cpuOptions.ThreadsPerCore) + } - privateIP := instanceOutput[instanceID].PrivateIpAddress - if privateIP != nil { - events[instanceID].MetricSetFields.Put("instance.private.ip", *privateIP) - } + publicIP := instanceOutput[instanceID].PublicIpAddress + if publicIP != nil { + events[instanceID].MetricSetFields.Put("instance.public.ip", *publicIP) + } - events[instanceID].MetricSetFields.Put("instance.image.id", *instanceOutput[instanceID].ImageId) - events[instanceID].MetricSetFields.Put("instance.state.name", instanceStateName) - events[instanceID].MetricSetFields.Put("instance.state.code", *instanceOutput[instanceID].State.Code) - events[instanceID].MetricSetFields.Put("instance.monitoring.state", monitoringState) - events[instanceID].MetricSetFields.Put("instance.public.dns_name", *instanceOutput[instanceID].PublicDnsName) - events[instanceID].MetricSetFields.Put("instance.private.dns_name", *instanceOutput[instanceID].PrivateDnsName) + privateIP := instanceOutput[instanceID].PrivateIpAddress + if privateIP != nil { + events[instanceID].MetricSetFields.Put("instance.private.ip", *privateIP) } + + events[instanceID].MetricSetFields.Put("instance.image.id", *instanceOutput[instanceID].ImageId) + events[instanceID].MetricSetFields.Put("instance.state.name", instanceStateName) + events[instanceID].MetricSetFields.Put("instance.state.code", *instanceOutput[instanceID].State.Code) + events[instanceID].MetricSetFields.Put("instance.monitoring.state", monitoringState) + events[instanceID].MetricSetFields.Put("instance.public.dns_name", *instanceOutput[instanceID].PublicDnsName) + events[instanceID].MetricSetFields.Put("instance.private.dns_name", *instanceOutput[instanceID].PrivateDnsName) } } diff --git a/x-pack/metricbeat/module/aws/rds/rds.go b/x-pack/metricbeat/module/aws/rds/rds.go index b381dcac943..3bb14f28de8 100644 --- a/x-pack/metricbeat/module/aws/rds/rds.go +++ b/x-pack/metricbeat/module/aws/rds/rds.go @@ -274,70 +274,72 @@ func (m *MetricSet) createCloudWatchEvents(getMetricDataResults []cloudwatch.Met // Find a timestamp for all metrics in output timestamp := aws.FindTimestamp(getMetricDataResults) - if !timestamp.IsZero() { - for _, output := range getMetricDataResults { - if len(output.Values) == 0 { - continue + if timestamp.IsZero() { + return nil, nil + } + + for _, output := range getMetricDataResults { + if len(output.Values) == 0 { + continue + } + exists, timestampIdx := aws.CheckTimestampInArray(timestamp, output.Timestamps) + if exists { + labels := strings.Split(*output.Label, " ") + // Collect dimension values from the labels and initialize events and metricSetFieldResults with dimValues + var dimValues string + for i := 1; i < len(labels); i += 2 { + dimValues = dimValues + labels[i+1] } - exists, timestampIdx := aws.CheckTimestampInArray(timestamp, output.Timestamps) - if exists { - labels := strings.Split(*output.Label, " ") - // Collect dimension values from the labels and initialize events and metricSetFieldResults with dimValues - var dimValues string - for i := 1; i < len(labels); i += 2 { - dimValues = dimValues + labels[i+1] - } - if _, ok := events[dimValues]; !ok { - events[dimValues] = aws.InitEvent(regionName, m.AccountName, m.AccountID) - } + if _, ok := events[dimValues]; !ok { + events[dimValues] = aws.InitEvent(regionName, m.AccountName, m.AccountID, timestamp) + } - if _, ok := metricSetFieldResults[dimValues]; !ok { - metricSetFieldResults[dimValues] = map[string]interface{}{} - } + if _, ok := metricSetFieldResults[dimValues]; !ok { + metricSetFieldResults[dimValues] = map[string]interface{}{} + } - if len(output.Values) > timestampIdx && len(labels) > 0 { - if labels[metricNameIdx] == "CPUUtilization" { - metricSetFieldResults[dimValues][labels[metricNameIdx]] = fmt.Sprint(output.Values[timestampIdx] / 100) - } else { - metricSetFieldResults[dimValues][labels[metricNameIdx]] = fmt.Sprint(output.Values[timestampIdx]) - } + if len(output.Values) > timestampIdx && len(labels) > 0 { + if labels[metricNameIdx] == "CPUUtilization" { + metricSetFieldResults[dimValues][labels[metricNameIdx]] = fmt.Sprint(output.Values[timestampIdx] / 100) + } else { + metricSetFieldResults[dimValues][labels[metricNameIdx]] = fmt.Sprint(output.Values[timestampIdx]) + } - for i := 1; i < len(labels); i += 2 { - if labels[i] == "DBInstanceIdentifier" { - dbIdentifier := labels[i+1] - if _, found := events[dbIdentifier]; found { - if _, found := dbInstanceMap[dbIdentifier]; !found { - delete(metricSetFieldResults, dimValues) - continue - } - events[dbIdentifier].RootFields.Put("cloud.availability_zone", dbInstanceMap[dbIdentifier].dbAvailabilityZone) - events[dbIdentifier].MetricSetFields.Put("db_instance.arn", dbInstanceMap[dbIdentifier].dbArn) - events[dbIdentifier].MetricSetFields.Put("db_instance.class", dbInstanceMap[dbIdentifier].dbClass) - events[dbIdentifier].MetricSetFields.Put("db_instance.identifier", dbInstanceMap[dbIdentifier].dbIdentifier) - events[dbIdentifier].MetricSetFields.Put("db_instance.status", dbInstanceMap[dbIdentifier].dbStatus) - - for _, tag := range dbInstanceMap[dbIdentifier].tags { - events[dbIdentifier].ModuleFields.Put("tags."+tag.Key, tag.Value) - } + for i := 1; i < len(labels); i += 2 { + if labels[i] == "DBInstanceIdentifier" { + dbIdentifier := labels[i+1] + if _, found := events[dbIdentifier]; found { + if _, found := dbInstanceMap[dbIdentifier]; !found { + delete(metricSetFieldResults, dimValues) + continue + } + events[dbIdentifier].RootFields.Put("cloud.availability_zone", dbInstanceMap[dbIdentifier].dbAvailabilityZone) + events[dbIdentifier].MetricSetFields.Put("db_instance.arn", dbInstanceMap[dbIdentifier].dbArn) + events[dbIdentifier].MetricSetFields.Put("db_instance.class", dbInstanceMap[dbIdentifier].dbClass) + events[dbIdentifier].MetricSetFields.Put("db_instance.identifier", dbInstanceMap[dbIdentifier].dbIdentifier) + events[dbIdentifier].MetricSetFields.Put("db_instance.status", dbInstanceMap[dbIdentifier].dbStatus) + + for _, tag := range dbInstanceMap[dbIdentifier].tags { + events[dbIdentifier].ModuleFields.Put("tags."+tag.Key, tag.Value) } } - metricSetFieldResults[dimValues][labels[i]] = fmt.Sprint(labels[(i + 1)]) + } + metricSetFieldResults[dimValues][labels[i]] = fmt.Sprint(labels[(i + 1)]) + } + + // if tags_filter is given, then only return metrics with DBInstanceIdentifier as dimension + if m.TagsFilter != nil { + if len(labels) == 1 { + delete(events, dimValues) + delete(metricSetFieldResults, dimValues) } - // if tags_filter is given, then only return metrics with DBInstanceIdentifier as dimension - if m.TagsFilter != nil { - if len(labels) == 1 { + for i := 1; i < len(labels); i += 2 { + if labels[i] != "DBInstanceIdentifier" && i == len(labels)-2 { delete(events, dimValues) delete(metricSetFieldResults, dimValues) } - - for i := 1; i < len(labels); i += 2 { - if labels[i] != "DBInstanceIdentifier" && i == len(labels)-2 { - delete(events, dimValues) - delete(metricSetFieldResults, dimValues) - } - } } } } diff --git a/x-pack/metricbeat/module/aws/s3_daily_storage/s3_daily_storage.go b/x-pack/metricbeat/module/aws/s3_daily_storage/s3_daily_storage.go index 53248284d41..d5efa36fb03 100644 --- a/x-pack/metricbeat/module/aws/s3_daily_storage/s3_daily_storage.go +++ b/x-pack/metricbeat/module/aws/s3_daily_storage/s3_daily_storage.go @@ -186,8 +186,6 @@ func createMetricDataQuery(metric cloudwatch.Metric, period time.Duration, index } func createCloudWatchEvents(outputs []cloudwatch.MetricDataResult, regionName string, bucketName string, accountName string, accountID string) (event mb.Event, err error) { - event = aws.InitEvent(regionName, accountName, accountID) - // AWS s3_daily_storage metrics mapOfMetricSetFieldResults := make(map[string]interface{}) @@ -214,6 +212,7 @@ func createCloudWatchEvents(outputs []cloudwatch.MetricDataResult, regionName st return } + event = aws.InitEvent(regionName, accountName, accountID, timestamp) event.MetricSetFields = resultMetricSetFields event.RootFields.Put("aws.s3.bucket.name", bucketName) return diff --git a/x-pack/metricbeat/module/aws/s3_request/s3_request.go b/x-pack/metricbeat/module/aws/s3_request/s3_request.go index 63b93f6cdf4..00b82827bbf 100644 --- a/x-pack/metricbeat/module/aws/s3_request/s3_request.go +++ b/x-pack/metricbeat/module/aws/s3_request/s3_request.go @@ -188,8 +188,6 @@ func constructMetricQueries(listMetricsOutputs []cloudwatch.Metric, period time. // CreateS3Events creates s3_request and s3_daily_storage events from Cloudwatch metric data. func createS3RequestEvents(outputs []cloudwatch.MetricDataResult, regionName string, bucketName string, accountName string, accountID string) (event mb.Event, err error) { - event = aws.InitEvent(regionName, accountName, accountID) - // AWS s3_request metrics mapOfMetricSetFieldResults := make(map[string]interface{}) @@ -216,6 +214,7 @@ func createS3RequestEvents(outputs []cloudwatch.MetricDataResult, regionName str return } + event = aws.InitEvent(regionName, accountName, accountID, timestamp) event.MetricSetFields = resultMetricSetFields event.RootFields.Put("aws.s3.bucket.name", bucketName) return diff --git a/x-pack/metricbeat/module/aws/sqs/sqs.go b/x-pack/metricbeat/module/aws/sqs/sqs.go index 6dc0774f66d..5f17eccb4b1 100644 --- a/x-pack/metricbeat/module/aws/sqs/sqs.go +++ b/x-pack/metricbeat/module/aws/sqs/sqs.go @@ -175,9 +175,7 @@ func createMetricDataQuery(metric cloudwatch.Metric, index int, period time.Dura return } -func createEventPerQueue(getMetricDataResults []cloudwatch.MetricDataResult, queueName string, metricsetName string, regionName string, schemaMetricFields s.Schema, accountName string, accountID string) (event mb.Event, err error) { - event = aws.InitEvent(regionName, accountName, accountID) - +func createEventPerQueue(getMetricDataResults []cloudwatch.MetricDataResult, queueName string, regionName string, schemaMetricFields s.Schema, accountName string, accountID string) (event mb.Event, err error) { // AWS sqs metrics mapOfMetricSetFieldResults := make(map[string]interface{}) @@ -204,6 +202,7 @@ func createEventPerQueue(getMetricDataResults []cloudwatch.MetricDataResult, que return } + event = aws.InitEvent(regionName, accountName, accountID, timestamp) event.MetricSetFields = resultMetricSetFields event.MetricSetFields.Put("queue.name", queueName) return @@ -213,7 +212,7 @@ func createSQSEvents(queueURLs []string, metricDataResults []cloudwatch.MetricDa for _, queueURL := range queueURLs { queueURLParsed := strings.Split(queueURL, "/") queueName := queueURLParsed[len(queueURLParsed)-1] - event, err := createEventPerQueue(metricDataResults, queueName, metricsetName, regionName, schemaRequestFields, accountName, accountID) + event, err := createEventPerQueue(metricDataResults, queueName, regionName, schemaRequestFields, accountName, accountID) if err != nil { event.Error = err report.Event(event) From aed4831afd215c8b230cda3836e71712fdf48389 Mon Sep 17 00:00:00 2001 From: Alex K <8418476+fearful-symmetry@users.noreply.github.com> Date: Tue, 6 Oct 2020 12:02:29 -0700 Subject: [PATCH 44/93] Remove nil-zero metrics and linux-exclusive metrics from Metricbeat (#21457) * refactor metricbeat to remove nil-zero metrics and linux-exclusive metrics * update xpack docs * fix non-linux diskstat builds * fix linux test builds * fix python tests * move windows files for disk performance * properly fix test_drop_fields * try to fix different system test * mage fmt * fix windows filesystem tests * fix platform test * add changelog --- CHANGELOG.next.asciidoc | 1 + .../system/diskio/disk_performance_386.go | 0 .../system/diskio/disk_performance_amd64.go | 0 .../metric}/system/diskio/diskstat.go | 11 +- .../metric}/system/diskio/diskstat_linux.go | 81 ++-- .../system/diskio/diskstat_linux_test.go | 60 +-- .../metric}/system/diskio/diskstat_other.go | 14 +- .../metric}/system/diskio/diskstat_windows.go | 14 +- .../system/diskio/diskstat_windows_helper.go | 0 .../system/diskio/diskstat_windows_test.go | 0 metricbeat/docs/fields.asciidoc | 354 +++++++++++++++++- metricbeat/docs/modules/linux.asciidoc | 10 + metricbeat/docs/modules/linux/iostat.asciidoc | 23 ++ metricbeat/docs/modules/linux/memory.asciidoc | 24 ++ metricbeat/docs/modules_list.asciidoc | 4 +- metricbeat/include/list_common.go | 2 + metricbeat/metricbeat.reference.yml | 2 + metricbeat/module/linux/_meta/config.yml | 2 + metricbeat/module/linux/_meta/fields.yml | 1 + metricbeat/module/linux/fields.go | 2 +- .../module/linux/iostat/_meta/data.json | 49 +++ .../module/linux/iostat/_meta/docs.asciidoc | 3 + .../module/linux/iostat/_meta/fields.yml | 61 +++ metricbeat/module/linux/iostat/data.go | 58 +++ metricbeat/module/linux/iostat/iostat.go | 104 +++++ metricbeat/module/linux/iostat/iostat_test.go | 55 +++ .../module/linux/memory/_meta/data.json | 59 +++ .../module/linux/memory/_meta/docs.asciidoc | 3 + .../module/linux/memory/_meta/fields.yml | 78 ++++ metricbeat/module/linux/memory/data.go | 99 +++++ metricbeat/module/linux/memory/memory.go | 62 +++ metricbeat/module/linux/memory/memory_test.go | 55 +++ metricbeat/module/system/cpu/_meta/data.json | 44 +-- metricbeat/module/system/cpu/cpu.go | 48 +-- metricbeat/module/system/cpu/data.go | 112 ++++++ .../module/system/diskio/_meta/data.json | 16 +- metricbeat/module/system/diskio/diskio.go | 57 ++- .../module/system/diskio/diskio_test.go | 40 ++ metricbeat/module/system/fields.go | 2 +- .../module/system/filesystem/_meta/data.json | 27 +- metricbeat/module/system/filesystem/helper.go | 9 +- .../module/system/fsstat/_meta/fields.yml | 2 +- metricbeat/module/system/fsstat/fsstat.go | 26 +- .../module/system/memory/_meta/data.json | 42 +-- metricbeat/module/system/memory/memory.go | 81 +--- .../module/system/process/_meta/data.json | 132 +++---- .../module/system/process/_meta/fields.yml | 16 +- metricbeat/module/system/process/process.go | 36 +- .../system/process_summary/_meta/data.json | 19 +- .../system/process_summary/process_summary.go | 32 +- .../process_summary/process_summary_test.go | 31 +- metricbeat/module/system/system.go | 38 +- metricbeat/module/system/test_system.py | 59 ++- metricbeat/modules.d/linux.yml.disabled | 2 + metricbeat/tests/system/test_processors.py | 15 +- x-pack/metricbeat/metricbeat.reference.yml | 2 + 56 files changed, 1700 insertions(+), 479 deletions(-) rename {metricbeat/module => libbeat/metric}/system/diskio/disk_performance_386.go (100%) rename {metricbeat/module => libbeat/metric}/system/diskio/disk_performance_amd64.go (100%) rename {metricbeat/module => libbeat/metric}/system/diskio/diskstat.go (88%) rename {metricbeat/module => libbeat/metric}/system/diskio/diskstat_linux.go (55%) rename {metricbeat/module => libbeat/metric}/system/diskio/diskstat_linux_test.go (55%) rename {metricbeat/module => libbeat/metric}/system/diskio/diskstat_other.go (79%) rename {metricbeat/module => libbeat/metric}/system/diskio/diskstat_windows.go (78%) rename {metricbeat/module => libbeat/metric}/system/diskio/diskstat_windows_helper.go (100%) rename {metricbeat/module => libbeat/metric}/system/diskio/diskstat_windows_test.go (100%) create mode 100644 metricbeat/docs/modules/linux/iostat.asciidoc create mode 100644 metricbeat/docs/modules/linux/memory.asciidoc create mode 100644 metricbeat/module/linux/iostat/_meta/data.json create mode 100644 metricbeat/module/linux/iostat/_meta/docs.asciidoc create mode 100644 metricbeat/module/linux/iostat/_meta/fields.yml create mode 100644 metricbeat/module/linux/iostat/data.go create mode 100644 metricbeat/module/linux/iostat/iostat.go create mode 100644 metricbeat/module/linux/iostat/iostat_test.go create mode 100644 metricbeat/module/linux/memory/_meta/data.json create mode 100644 metricbeat/module/linux/memory/_meta/docs.asciidoc create mode 100644 metricbeat/module/linux/memory/_meta/fields.yml create mode 100644 metricbeat/module/linux/memory/data.go create mode 100644 metricbeat/module/linux/memory/memory.go create mode 100644 metricbeat/module/linux/memory/memory_test.go create mode 100644 metricbeat/module/system/cpu/data.go diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 3099a033153..6b4a999e0ec 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -93,6 +93,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Move service config under metrics and simplify metric types. {pull}18691[18691] - Fix ECS compliance of user.id field in system/users metricset {pull}19019[19019] - Rename googlecloud stackdriver metricset to metrics. {pull}19718[19718] +- Remove "invalid zero" metrics on Windows and Darwin, don't report linux-only memory and diskio metrics when running under agent. {pull}21457[21457] *Packetbeat* diff --git a/metricbeat/module/system/diskio/disk_performance_386.go b/libbeat/metric/system/diskio/disk_performance_386.go similarity index 100% rename from metricbeat/module/system/diskio/disk_performance_386.go rename to libbeat/metric/system/diskio/disk_performance_386.go diff --git a/metricbeat/module/system/diskio/disk_performance_amd64.go b/libbeat/metric/system/diskio/disk_performance_amd64.go similarity index 100% rename from metricbeat/module/system/diskio/disk_performance_amd64.go rename to libbeat/metric/system/diskio/disk_performance_amd64.go diff --git a/metricbeat/module/system/diskio/diskstat.go b/libbeat/metric/system/diskio/diskstat.go similarity index 88% rename from metricbeat/module/system/diskio/diskstat.go rename to libbeat/metric/system/diskio/diskstat.go index 949d69778fa..3cea37c552a 100644 --- a/metricbeat/module/system/diskio/diskstat.go +++ b/libbeat/metric/system/diskio/diskstat.go @@ -25,11 +25,11 @@ import ( sigar "github.com/elastic/gosigar" ) -// mapping fields which output by `iostat -x` on linux +// IOMetric contains mapping fields which are outputed by `iostat -x` on linux // // Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await r_await w_await svctm %util // sda 0.06 0.78 0.09 0.27 9.42 8.06 48.64 0.00 1.34 0.99 1.45 0.77 0.03 -type DiskIOMetric struct { +type IOMetric struct { ReadRequestMergeCountPerSec float64 `json:"rrqmCps"` WriteRequestMergeCountPerSec float64 `json:"wrqmCps"` ReadRequestCountPerSec float64 `json:"rrqCps"` @@ -46,8 +46,9 @@ type DiskIOMetric struct { BusyPct float64 `json:"busy"` } -type DiskIOStat struct { +// IOStat carries disk statistics for all devices +type IOStat struct { lastDiskIOCounters map[string]disk.IOCountersStat - lastCpu sigar.Cpu - curCpu sigar.Cpu + lastCPU sigar.Cpu + curCPU sigar.Cpu } diff --git a/metricbeat/module/system/diskio/diskstat_linux.go b/libbeat/metric/system/diskio/diskstat_linux.go similarity index 55% rename from metricbeat/module/system/diskio/diskstat_linux.go rename to libbeat/metric/system/diskio/diskstat_linux.go index 4ecfdf700ac..826aed78c27 100644 --- a/metricbeat/module/system/diskio/diskstat_linux.go +++ b/libbeat/metric/system/diskio/diskstat_linux.go @@ -26,7 +26,8 @@ import ( "github.com/elastic/beats/v7/libbeat/metric/system/cpu" ) -func Get_CLK_TCK() uint32 { +// GetCLKTCK emulates the _SC_CLK_TCK syscall +func GetCLKTCK() uint32 { // return uint32(C.sysconf(C._SC_CLK_TCK)) // NOTE: _SC_CLK_TCK should be fetched from sysconf using cgo return uint32(100) @@ -38,77 +39,78 @@ func IOCounters(names ...string) (map[string]disk.IOCountersStat, error) { } // NewDiskIOStat :init DiskIOStat object. -func NewDiskIOStat() *DiskIOStat { - return &DiskIOStat{ +func NewDiskIOStat() *IOStat { + return &IOStat{ lastDiskIOCounters: map[string]disk.IOCountersStat{}, } } // OpenSampling creates current cpu sampling // need call as soon as get IOCounters. -func (stat *DiskIOStat) OpenSampling() error { - return stat.curCpu.Get() +func (stat *IOStat) OpenSampling() error { + return stat.curCPU.Get() } -// CalIOStatistics calculates IO statistics. -func (stat *DiskIOStat) CalIOStatistics(result *DiskIOMetric, counter disk.IOCountersStat) error { +// CalcIOStatistics calculates IO statistics. +func (stat *IOStat) CalcIOStatistics(counter disk.IOCountersStat) (IOMetric, error) { var last disk.IOCountersStat var ok bool // if last counter not found, create one and return all 0 if last, ok = stat.lastDiskIOCounters[counter.Name]; !ok { stat.lastDiskIOCounters[counter.Name] = counter - return nil + return IOMetric{}, nil } // calculate the delta ms between the CloseSampling and OpenSampling - deltams := 1000.0 * float64(stat.curCpu.Total()-stat.lastCpu.Total()) / float64(cpu.NumCores) / float64(Get_CLK_TCK()) + deltams := 1000.0 * float64(stat.curCPU.Total()-stat.lastCPU.Total()) / float64(cpu.NumCores) / float64(GetCLKTCK()) if deltams <= 0 { - return errors.New("The delta cpu time between close sampling and open sampling is less or equal to 0") + return IOMetric{}, errors.New("The delta cpu time between close sampling and open sampling is less or equal to 0") } - rd_ios := counter.ReadCount - last.ReadCount - rd_merges := counter.MergedReadCount - last.MergedReadCount - rd_bytes := counter.ReadBytes - last.ReadBytes - rd_ticks := counter.ReadTime - last.ReadTime - wr_ios := counter.WriteCount - last.WriteCount - wr_merges := counter.MergedWriteCount - last.MergedWriteCount - wr_bytes := counter.WriteBytes - last.WriteBytes - wr_ticks := counter.WriteTime - last.WriteTime + rdIOs := counter.ReadCount - last.ReadCount + rdMerges := counter.MergedReadCount - last.MergedReadCount + rdBytes := counter.ReadBytes - last.ReadBytes + rdTicks := counter.ReadTime - last.ReadTime + wrIOs := counter.WriteCount - last.WriteCount + wrMerges := counter.MergedWriteCount - last.MergedWriteCount + wrBytes := counter.WriteBytes - last.WriteBytes + wrTicks := counter.WriteTime - last.WriteTime ticks := counter.IoTime - last.IoTime aveq := counter.WeightedIO - last.WeightedIO - n_ios := rd_ios + wr_ios - n_ticks := rd_ticks + wr_ticks - n_bytes := rd_bytes + wr_bytes + nIOs := rdIOs + wrIOs + nTicks := rdTicks + wrTicks + nBytes := rdBytes + wrBytes size := float64(0) wait := float64(0) svct := float64(0) - if n_ios > 0 { - size = float64(n_bytes) / float64(n_ios) - wait = float64(n_ticks) / float64(n_ios) - svct = float64(ticks) / float64(n_ios) + if nIOs > 0 { + size = float64(nBytes) / float64(nIOs) + wait = float64(nTicks) / float64(nIOs) + svct = float64(ticks) / float64(nIOs) } queue := float64(aveq) / deltams - per_sec := func(x uint64) float64 { + perSec := func(x uint64) float64 { return 1000.0 * float64(x) / deltams } - result.ReadRequestMergeCountPerSec = per_sec(rd_merges) - result.WriteRequestMergeCountPerSec = per_sec(wr_merges) - result.ReadRequestCountPerSec = per_sec(rd_ios) - result.WriteRequestCountPerSec = per_sec(wr_ios) - result.ReadBytesPerSec = per_sec(rd_bytes) - result.WriteBytesPerSec = per_sec(wr_bytes) + result := IOMetric{} + result.ReadRequestMergeCountPerSec = perSec(rdMerges) + result.WriteRequestMergeCountPerSec = perSec(wrMerges) + result.ReadRequestCountPerSec = perSec(rdIOs) + result.WriteRequestCountPerSec = perSec(wrIOs) + result.ReadBytesPerSec = perSec(rdBytes) + result.WriteBytesPerSec = perSec(wrBytes) result.AvgRequestSize = size result.AvgQueueSize = queue result.AvgAwaitTime = wait - if rd_ios > 0 { - result.AvgReadAwaitTime = float64(rd_ticks) / float64(rd_ios) + if rdIOs > 0 { + result.AvgReadAwaitTime = float64(rdTicks) / float64(rdIOs) } - if wr_ios > 0 { - result.AvgWriteAwaitTime = float64(wr_ticks) / float64(wr_ios) + if wrIOs > 0 { + result.AvgWriteAwaitTime = float64(wrTicks) / float64(wrIOs) } result.AvgServiceTime = svct result.BusyPct = 100.0 * float64(ticks) / deltams @@ -117,10 +119,11 @@ func (stat *DiskIOStat) CalIOStatistics(result *DiskIOMetric, counter disk.IOCou } stat.lastDiskIOCounters[counter.Name] = counter - return nil + return result, nil } -func (stat *DiskIOStat) CloseSampling() { - stat.lastCpu = stat.curCpu +// CloseSampling closes the disk sampler +func (stat *IOStat) CloseSampling() { + stat.lastCPU = stat.curCPU } diff --git a/metricbeat/module/system/diskio/diskstat_linux_test.go b/libbeat/metric/system/diskio/diskstat_linux_test.go similarity index 55% rename from metricbeat/module/system/diskio/diskstat_linux_test.go rename to libbeat/metric/system/diskio/diskstat_linux_test.go index 56a8d9dc7cc..74c0bef231e 100644 --- a/metricbeat/module/system/diskio/diskstat_linux_test.go +++ b/libbeat/metric/system/diskio/diskstat_linux_test.go @@ -27,53 +27,11 @@ import ( "github.com/stretchr/testify/assert" sigar "github.com/elastic/gosigar" - - mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" - "github.com/elastic/beats/v7/metricbeat/module/system" ) -func Test_Get_CLK_TCK(t *testing.T) { +func Test_GetCLKTCK(t *testing.T) { //usually the tick is 100 - assert.Equal(t, uint32(100), Get_CLK_TCK()) -} - -func TestDataNameFilter(t *testing.T) { - oldFS := system.HostFS - newFS := "_meta/testdata" - system.HostFS = &newFS - defer func() { - system.HostFS = oldFS - }() - - conf := map[string]interface{}{ - "module": "system", - "metricsets": []string{"diskio"}, - "diskio.include_devices": []string{"sda", "sda1", "sda2"}, - } - - f := mbtest.NewReportingMetricSetV2Error(t, conf) - data, errs := mbtest.ReportingFetchV2Error(f) - assert.Empty(t, errs) - assert.Equal(t, 3, len(data)) -} - -func TestDataEmptyFilter(t *testing.T) { - oldFS := system.HostFS - newFS := "_meta/testdata" - system.HostFS = &newFS - defer func() { - system.HostFS = oldFS - }() - - conf := map[string]interface{}{ - "module": "system", - "metricsets": []string{"diskio"}, - } - - f := mbtest.NewReportingMetricSetV2Error(t, conf) - data, errs := mbtest.ReportingFetchV2Error(f) - assert.Empty(t, errs) - assert.Equal(t, 10, len(data)) + assert.Equal(t, uint32(100), GetCLKTCK()) } func TestDiskIOStat_CalIOStatistics(t *testing.T) { @@ -85,9 +43,9 @@ func TestDiskIOStat_CalIOStatistics(t *testing.T) { Name: "iostat", } - stat := &DiskIOStat{ + stat := &IOStat{ lastDiskIOCounters: map[string]disk.IOCountersStat{ - "iostat": disk.IOCountersStat{ + "iostat": { ReadCount: 3, WriteCount: 5, ReadTime: 7, @@ -95,17 +53,17 @@ func TestDiskIOStat_CalIOStatistics(t *testing.T) { Name: "iostat", }, }, - lastCpu: sigar.Cpu{Idle: 100}, - curCpu: sigar.Cpu{Idle: 1}, + lastCPU: sigar.Cpu{Idle: 100}, + curCPU: sigar.Cpu{Idle: 1}, } - expected := DiskIOMetric{ + expected := IOMetric{ AvgAwaitTime: 24.0 / 22.0, AvgReadAwaitTime: 1.2, AvgWriteAwaitTime: 1, } - var got DiskIOMetric - err := stat.CalIOStatistics(&got, counter) + + got, err := stat.CalcIOStatistics(counter) if err != nil { t.Fatal(err) } diff --git a/metricbeat/module/system/diskio/diskstat_other.go b/libbeat/metric/system/diskio/diskstat_other.go similarity index 79% rename from metricbeat/module/system/diskio/diskstat_other.go rename to libbeat/metric/system/diskio/diskstat_other.go index d2669117d9e..ad5208b1b4f 100644 --- a/metricbeat/module/system/diskio/diskstat_other.go +++ b/libbeat/metric/system/diskio/diskstat_other.go @@ -25,24 +25,24 @@ import ( ) // NewDiskIOStat :init DiskIOStat object. -func NewDiskIOStat() *DiskIOStat { - return &DiskIOStat{ +func NewDiskIOStat() *IOStat { + return &IOStat{ lastDiskIOCounters: map[string]disk.IOCountersStat{}, } } // OpenSampling stub for linux implementation. -func (stat *DiskIOStat) OpenSampling() error { +func (stat *IOStat) OpenSampling() error { return nil } -// CalIOStatistics stub for linux implementation. -func (stat *DiskIOStat) CalIOStatistics(result *DiskIOMetric, counter disk.IOCountersStat) error { - return errors.New("not implemented out of linux") +// CalcIOStatistics stub for linux implementation. +func (stat *IOStat) CalcIOStatistics(rcounter disk.IOCountersStat) (IOMetric, error) { + return IOMetric{}, errors.New("not implemented out of linux") } // CloseSampling stub for linux implementation. -func (stat *DiskIOStat) CloseSampling() {} +func (stat *IOStat) CloseSampling() {} // IOCounters should map functionality to disk package for linux os. func IOCounters(names ...string) (map[string]disk.IOCountersStat, error) { diff --git a/metricbeat/module/system/diskio/diskstat_windows.go b/libbeat/metric/system/diskio/diskstat_windows.go similarity index 78% rename from metricbeat/module/system/diskio/diskstat_windows.go rename to libbeat/metric/system/diskio/diskstat_windows.go index 1e2b363074b..665caab9130 100644 --- a/metricbeat/module/system/diskio/diskstat_windows.go +++ b/libbeat/metric/system/diskio/diskstat_windows.go @@ -25,24 +25,24 @@ import ( ) // NewDiskIOStat :init DiskIOStat object. -func NewDiskIOStat() *DiskIOStat { - return &DiskIOStat{ +func NewDiskIOStat() *IOStat { + return &IOStat{ lastDiskIOCounters: map[string]disk.IOCountersStat{}, } } // OpenSampling stub for linux implementation. -func (stat *DiskIOStat) OpenSampling() error { +func (stat *IOStat) OpenSampling() error { return nil } -// CalIOStatistics stub for linux implementation. -func (stat *DiskIOStat) CalIOStatistics(result *DiskIOMetric, counter disk.IOCountersStat) error { - return errors.New("iostat is not implement for Windows") +// CalcIOStatistics stub for linux implementation. +func (stat *IOStat) CalcIOStatistics(counter disk.IOCountersStat) (IOMetric, error) { + return IOMetric{}, errors.New("iostat is not implement for Windows") } // CloseSampling stub for linux implementation. -func (stat *DiskIOStat) CloseSampling() {} +func (stat *IOStat) CloseSampling() {} // IOCounters should map functionality to disk package for linux os. func IOCounters(names ...string) (map[string]disk.IOCountersStat, error) { diff --git a/metricbeat/module/system/diskio/diskstat_windows_helper.go b/libbeat/metric/system/diskio/diskstat_windows_helper.go similarity index 100% rename from metricbeat/module/system/diskio/diskstat_windows_helper.go rename to libbeat/metric/system/diskio/diskstat_windows_helper.go diff --git a/metricbeat/module/system/diskio/diskstat_windows_test.go b/libbeat/metric/system/diskio/diskstat_windows_test.go similarity index 100% rename from metricbeat/module/system/diskio/diskstat_windows_test.go rename to libbeat/metric/system/diskio/diskstat_windows_test.go diff --git a/metricbeat/docs/fields.asciidoc b/metricbeat/docs/fields.asciidoc index dc7c0c45d3e..7f1d0f69ab9 100644 --- a/metricbeat/docs/fields.asciidoc +++ b/metricbeat/docs/fields.asciidoc @@ -29167,6 +29167,7 @@ linux module [float] === linux +linux system metrics @@ -29264,6 +29265,147 @@ type: long -- +[float] +=== iostat + +iostat + + + +*`linux.iostat.read.request.merges_per_sec`*:: ++ +-- +The number of read requests merged per second that were queued to the device. + + +type: float + +-- + +*`linux.iostat.write.request.merges_per_sec`*:: ++ +-- +The number of write requests merged per second that were queued to the device. + + +type: float + +-- + +*`linux.iostat.read.request.per_sec`*:: ++ +-- +The number of read requests that were issued to the device per second + + +type: float + +-- + +*`linux.iostat.write.request.per_sec`*:: ++ +-- +The number of write requests that were issued to the device per second + + +type: float + +-- + +*`linux.iostat.read.per_sec.bytes`*:: ++ +-- +The number of Bytes read from the device per second. + + +type: float + +format: bytes + +-- + +*`linux.iostat.read.await`*:: ++ +-- +The average time spent for read requests issued to the device to be served. + + +type: float + +-- + +*`linux.iostat.write.per_sec.bytes`*:: ++ +-- +The number of Bytes write from the device per second. + + +type: float + +format: bytes + +-- + +*`linux.iostat.write.await`*:: ++ +-- +The average time spent for write requests issued to the device to be served. + + +type: float + +-- + +*`linux.iostat.request.avg_size`*:: ++ +-- +The average size (in bytes) of the requests that were issued to the device. + + +type: float + +-- + +*`linux.iostat.queue.avg_size`*:: ++ +-- +The average queue length of the requests that were issued to the device. + + +type: float + +-- + +*`linux.iostat.await`*:: ++ +-- +The average time spent for requests issued to the device to be served. + + +type: float + +-- + +*`linux.iostat.service_time`*:: ++ +-- +The average service time (in milliseconds) for I/O requests that were issued to the device. + + +type: float + +-- + +*`linux.iostat.busy`*:: ++ +-- +Percentage of CPU time during which I/O requests were issued to the device (bandwidth utilization for the device). Device saturation occurs when this value is close to 100%. + + +type: float + +-- + [float] === ksm @@ -29338,6 +29480,186 @@ type: long -- +[float] +=== memory + +Linux memory data + + + +[float] +=== page_stats + +memory page statistics + + +*`linux.memory.page_stats.pgscan_kswapd.pages`*:: ++ +-- +pages scanned by kswapd + +type: long + +format: number + +-- + +*`linux.memory.page_stats.pgscan_direct.pages`*:: ++ +-- +pages scanned directly + +type: long + +format: number + +-- + +*`linux.memory.page_stats.pgfree.pages`*:: ++ +-- +pages freed by the system + +type: long + +format: number + +-- + +*`linux.memory.page_stats.pgsteal_kswapd.pages`*:: ++ +-- +number of pages reclaimed by kswapd + +type: long + +format: number + +-- + +*`linux.memory.page_stats.pgsteal_direct.pages`*:: ++ +-- +number of pages reclaimed directly + +type: long + +format: number + +-- + +*`linux.memory.page_stats.direct_efficiency.pct`*:: ++ +-- +direct reclaim efficiency percentage. A lower percentage indicates the system is struggling to reclaim memory. + +type: scaled_float + +format: percent + +-- + +*`linux.memory.page_stats.kswapd_efficiency.pct`*:: ++ +-- +kswapd reclaim efficiency percentage. A lower percentage indicates the system is struggling to reclaim memory. + +type: scaled_float + +format: percent + +-- + +[float] +=== hugepages + +This group contains statistics related to huge pages usage on the system. + + +*`linux.memory.hugepages.total`*:: ++ +-- +Number of huge pages in the pool. + + +type: long + +format: number + +-- + +*`linux.memory.hugepages.used.bytes`*:: ++ +-- +Memory used in allocated huge pages. + + +type: long + +format: bytes + +-- + +*`linux.memory.hugepages.used.pct`*:: ++ +-- +Percentage of huge pages used. + + +type: long + +format: percent + +-- + +*`linux.memory.hugepages.free`*:: ++ +-- +Number of available huge pages in the pool. + + +type: long + +format: number + +-- + +*`linux.memory.hugepages.reserved`*:: ++ +-- +Number of reserved but not allocated huge pages in the pool. + + +type: long + +format: number + +-- + +*`linux.memory.hugepages.surplus`*:: ++ +-- +Number of overcommited huge pages. + + +type: long + +format: number + +-- + +*`linux.memory.hugepages.default_size`*:: ++ +-- +Default size for huge pages. + + +type: long + +format: bytes + +-- + [float] === pageinfo @@ -40709,7 +41031,7 @@ type: long *`system.fsstat.total_files`*:: + -- -Total number of files. +Total number of files. Not on Windows. type: long @@ -41571,7 +41893,7 @@ format: bytes *`system.process.memory.rss.bytes`*:: + -- -The Resident Set Size. The amount of memory the process occupied in main memory (RAM). On Windows this represents the current working set size, in bytes. +The Resident Set Size. The amount of memory the process occupied in main memory (RAM). On Windows this represents the current working set size, in bytes. Not available on Windows. type: long @@ -41583,7 +41905,31 @@ format: bytes *`system.process.memory.rss.pct`*:: + -- -The percentage of memory the process occupied in main memory (RAM). +The percentage of memory the process occupied in main memory (RAM). Not available on Windows. + + +type: scaled_float + +format: percent + +-- + +*`system.process.memory.wss.bytes`*:: ++ +-- +The Working Set Size. The amount of memory the process occupied in main memory (RAM). Windows only. + + +type: long + +format: bytes + +-- + +*`system.process.memory.wss.pct`*:: ++ +-- +The percentage of memory the process occupied in main memory (RAM). Windows only. type: scaled_float @@ -41595,7 +41941,7 @@ format: percent *`system.process.memory.share`*:: + -- -The shared memory the process uses. +The shared memory the process uses. Not available on Windows. type: long diff --git a/metricbeat/docs/modules/linux.asciidoc b/metricbeat/docs/modules/linux.asciidoc index d63528730cb..ab911885afd 100644 --- a/metricbeat/docs/modules/linux.asciidoc +++ b/metricbeat/docs/modules/linux.asciidoc @@ -22,8 +22,10 @@ metricbeat.modules: period: 10s metricsets: - "pageinfo" + - "memory" # - ksm # - conntrack + # - iostat enabled: true #hostfs: /hostfs @@ -36,13 +38,21 @@ The following metricsets are available: * <> +* <> + * <> +* <> + * <> include::linux/conntrack.asciidoc[] +include::linux/iostat.asciidoc[] + include::linux/ksm.asciidoc[] +include::linux/memory.asciidoc[] + include::linux/pageinfo.asciidoc[] diff --git a/metricbeat/docs/modules/linux/iostat.asciidoc b/metricbeat/docs/modules/linux/iostat.asciidoc new file mode 100644 index 00000000000..7a41297b819 --- /dev/null +++ b/metricbeat/docs/modules/linux/iostat.asciidoc @@ -0,0 +1,23 @@ +//// +This file is generated! See scripts/mage/docs_collector.go +//// + +[[metricbeat-metricset-linux-iostat]] +=== linux iostat metricset + +beta[] + +include::../../../module/linux/iostat/_meta/docs.asciidoc[] + + +==== Fields + +For a description of each field in the metricset, see the +<> section. + +Here is an example document generated by this metricset: + +[source,json] +---- +include::../../../module/linux/iostat/_meta/data.json[] +---- diff --git a/metricbeat/docs/modules/linux/memory.asciidoc b/metricbeat/docs/modules/linux/memory.asciidoc new file mode 100644 index 00000000000..9ea3d482e57 --- /dev/null +++ b/metricbeat/docs/modules/linux/memory.asciidoc @@ -0,0 +1,24 @@ +//// +This file is generated! See scripts/mage/docs_collector.go +//// + +[[metricbeat-metricset-linux-memory]] +=== linux memory metricset + +beta[] + +include::../../../module/linux/memory/_meta/docs.asciidoc[] + +This is a default metricset. If the host module is unconfigured, this metricset is enabled by default. + +==== Fields + +For a description of each field in the metricset, see the +<> section. + +Here is an example document generated by this metricset: + +[source,json] +---- +include::../../../module/linux/memory/_meta/data.json[] +---- diff --git a/metricbeat/docs/modules_list.asciidoc b/metricbeat/docs/modules_list.asciidoc index 5ff2305665f..b37f7f831a3 100644 --- a/metricbeat/docs/modules_list.asciidoc +++ b/metricbeat/docs/modules_list.asciidoc @@ -176,8 +176,10 @@ This file is generated! See scripts/mage/docs_collector.go .2+| .2+| |<> beta[] |<> beta[] |<> beta[] |image:./images/icon-no.png[No prebuilt dashboards] | -.3+| .3+| |<> beta[] +.5+| .5+| |<> beta[] +|<> beta[] |<> beta[] +|<> beta[] |<> beta[] |<> |image:./images/icon-no.png[No prebuilt dashboards] | .2+| .2+| |<> diff --git a/metricbeat/include/list_common.go b/metricbeat/include/list_common.go index 39fadeea0e0..b77bb57f9a6 100644 --- a/metricbeat/include/list_common.go +++ b/metricbeat/include/list_common.go @@ -95,7 +95,9 @@ import ( _ "github.com/elastic/beats/v7/metricbeat/module/kvm/status" _ "github.com/elastic/beats/v7/metricbeat/module/linux" _ "github.com/elastic/beats/v7/metricbeat/module/linux/conntrack" + _ "github.com/elastic/beats/v7/metricbeat/module/linux/iostat" _ "github.com/elastic/beats/v7/metricbeat/module/linux/ksm" + _ "github.com/elastic/beats/v7/metricbeat/module/linux/memory" _ "github.com/elastic/beats/v7/metricbeat/module/linux/pageinfo" _ "github.com/elastic/beats/v7/metricbeat/module/logstash" _ "github.com/elastic/beats/v7/metricbeat/module/logstash/node" diff --git a/metricbeat/metricbeat.reference.yml b/metricbeat/metricbeat.reference.yml index 9b6f37eb447..9df18e4d7f9 100644 --- a/metricbeat/metricbeat.reference.yml +++ b/metricbeat/metricbeat.reference.yml @@ -572,8 +572,10 @@ metricbeat.modules: period: 10s metricsets: - "pageinfo" + - "memory" # - ksm # - conntrack + # - iostat enabled: true #hostfs: /hostfs diff --git a/metricbeat/module/linux/_meta/config.yml b/metricbeat/module/linux/_meta/config.yml index e00fe571846..490d3245c19 100644 --- a/metricbeat/module/linux/_meta/config.yml +++ b/metricbeat/module/linux/_meta/config.yml @@ -2,8 +2,10 @@ period: 10s metricsets: - "pageinfo" + - "memory" # - ksm # - conntrack + # - iostat enabled: true #hostfs: /hostfs diff --git a/metricbeat/module/linux/_meta/fields.yml b/metricbeat/module/linux/_meta/fields.yml index c3fe9657e89..44236f625e9 100644 --- a/metricbeat/module/linux/_meta/fields.yml +++ b/metricbeat/module/linux/_meta/fields.yml @@ -7,4 +7,5 @@ - name: linux type: group description: > + linux system metrics fields: diff --git a/metricbeat/module/linux/fields.go b/metricbeat/module/linux/fields.go index e4935eb2d7f..90e7e3ed940 100644 --- a/metricbeat/module/linux/fields.go +++ b/metricbeat/module/linux/fields.go @@ -32,5 +32,5 @@ func init() { // AssetLinux returns asset data. // This is the base64 encoded gzipped contents of module/linux. func AssetLinux() string { - return "eJy8l0tv4zYQx+/+FIMcF91Hsm8fCgTdRVEUKYIGe+mhwogcWawpUuWQybqfviBl2YoiOUpTiZcgIfWfn/6ch/IStrRbg1YmfF8BeOU1reEs/X62AnCkCZnWkJPHFYAkFk7VXlmzhh9XANA8C5WVQdMKoFCkJa/T1kswWNFRPi6/q2kNG2dDvf/LgOZ9ma6UsMZ4h2J72BmSjKuP3q7BcM0aEu+DdGE4VBW63b29MZxHQse1lwNbgCmyAwywR6/YK8E/pDMkAYWzzPDT9TcQ1hH3tIagu+DS2T7bkVxbsxnYfAQ+rhrFljwn+ZokyEDg7dFWKFBpFRyNghE6vctmwjtykPFO0RHUW6hwS+CsraCwDgzdgTUPfO2ANgozULZsyoAvqQPtMdfjzhU2GDkDDgchiLkIWu+ACZ0oSY6+fkujNsYOXPP/l2JMZAC1I5S75BEJ31wk9u65X54dSMPkfBaTkuaw7rdQ5eRiObd3eleSI9CK/T54+6NhgBOot6jVMyEfyj9w1JfoQaAx1kNOkFwc8ObQAFM+ZI7Yo/MzWJhyHrS121BH+5QoocR0zznBPu6x0zTHHbH6p5OcLe2Wqzmmxn3Zk/PCo+/XzH+eFr/eXHUmwxMHQI0b4oxLdLNk/k0SbqLERhaYXk1gUYPxng2jPDHs9Ztoj8EEM5s134z6O9AJjAoPGLdWo1cDTf/5GNfpakSJZhNd8dZCgez3hdW8/bhLcRpkLNDMMQCPbdOrihgqcptU1006lXhLkMdmFQHMKUxO/SAzVlImSlSz4DZOpsaZ0BxhGpEVfs8icZvZ0zBlqOf1VIZaK4GxacYO0svDbhkoU9g5+uWA9qmmmQcpd1nvgXGgCYZ8QY9QOFvB69pZ8TpFiAEasVgPgQ/tK9+BdZLcEzvsl6vL0XscYp7AndivLhNXeomBE2NYXbSzN2eD+4+k2UTCROGIQJTBbDnm3MWfb15cX/78Nbv55Y+vp9HOF0c7n4p2sTjaxVS0t4ujvZ2K9m5xtHdT0d4vjvZ+KtqHxdE+TEX7uDjax6lonxZH+zQV7fPiaJ8nt9zlx8H52DxooeK3EL96MTjxbf4Xif4/mxNIfsc7QK2twHgK0sDvfAXEqRoD3PvSWP0bAAD//4Pi/m8=" + return "eJzEmd9v2zgSx9/zVwwCHNAWVzfp7/rhgNylOBR3uQu27csudoUxObK5pkiVP+y6f/2CpBXLtmQrcaT6pWgkz3z4neHMkH4Oc1qNQQrlv58BOOEkjeE8/v/8DMCQJLQ0hgk5PAPgZJkRpRNajeEfZwCQvguF5l7SGUAuSHI7jo+eg8KCNubDx61KGsPUaF+u/9Jgc2PXrqyjAgpyRjC7flj3UffDtFLOIJvfPWnyFz6766o+LSzh02R8F6QOY31RoFltPWvDOeI6fNbmQOeg8uwOBqxDJ6wTzP49vkMckBltLfzr9iswbcju2GqCroNzo3fZNuRSq2nDwyPw4VMim5Oz0XxJHLgncHojK+QopPCGWsEIjVxlPeFtOEg5I2gD6jQUOCcwWheQawOKlqDVnq410GShB8qKTShwM6pBO5zIduVy7RXvAcd6xsja3Eu5Akto2Ix46/IrGjFVuiHMj5dilkgBSkPIV1EjYi4FEnfivLs9a5DKknFZSErqQ7r/+WJCJmznKqbLGRkCKaxbO6/+SQxwAHWBUpwIuW9+T1E3QwcMldIOJgRRxQZt7gpgzIfMkHVoXA8SxpwHqfXcl0E+wWYwwxjnCcHa76bSpNcNWfGjlpx3IupQSftoHHuWD3WNkLMjQ988WTcqyEzJZiWZzBJr7CS51Lir7VHpvswI1F3+BZewdmkh+uRQkgFLTCuewr4MufnNk0/7KBQfTgvBaNS4jKURjgZeR/T52AvZiseggdjQCmv3aGvr6hCAYZU/jTwqvgYeTVZur5E8GvY/g/Gkem500cw42p2etCnQjWGfbGsBuESxi3ciOC7I4JTAiYLAlqRcnEa2s6ZR8VQQLZkF8UP7dUDVU8o8puxpCcPpvpP0DxS+2qG4mGahMfWDHizDE6GSfE9DGAJlxx3bTB5raM/c0QdIUlM3exToIbflaYkRnglGWTDbU1IkDwk8JEchpBRp+9mncRGfXvz/NL0n3jafgB9Ef0uGkXIBXufxfBvZuTdCTdcD4BZyexN6MkHFl4K7GXgnpPiBwW1c9OatpyO4Tq9bdN6kVzRj3sRpPUzEwsICpQ9egEltY2gvLy7+ttFjb9Sc26KPOXPb7MGrCYeuub4/4GLiP59vapcQ97xrKDEMhnaGppdD1udoOHkJZ2ZvabexNLGIRn8nw4jQ+Nb2k7djMF71Js1XJb55OoBR4B3GQkt0ouF+4XSM2xgaNkM1Dao4rSFH66oCGVffrlLupcwsQ9XHXcvmhB6qTDpQxCNkSqcZLggm4VwcANQhTBuPnpnSnDI2Q9ELblIyFumIZgjjbUyB37NAXGV2N0zuy3415b6UgmE4n4cKspOHFVJBhd66pHm0avnfdG0d7QPHra8dqp1JyocX0LXHMnbgB5fNaci5bG6XWPJR1O6+waoG6jSYH4tmSvl1osNkBcn1MUAuDDE3PGDyK9tv98ppboiGAwveom5huEi/aRzSzhHKAaO7OZwlWkNMoii6RjrSDhfqdtqjYU8vZJTngglSbDUqWfvNpGUoiWdNo2qdukxT6THs5LuihQ1DZQCnNIIrkHpJpvY3EIrHQmlryRPGTeuMn05lapt3dlN9aS/yKZw/R4Lk+6dIUC1/5qfUlKPt1bs0lIvvYzj/Larw+/mh6v4lHAiiFWBaudDqa1U+9Clc/xIRQNYJ7G080aja4vauP440BKcdyn633bGGXlvQ+tepUmvZnojeEm+8aerM3fblDtg3qQsHhkCLUuo0i2xWcYT80K45wt1xt7SMeVtn4K0sOjgpG7r3+P64+YELFDLOz/fNFEPpouTn8lcUMPEOlHaNSdNtQdabUvqe2+Sx9egFGaaLQnRNe045eumarvs6o5+wZa+T+3SXmWvTyFyf0oXKdR8nhwbbhw4ME8/5Ktv5QjtQBzWu0WG6On9RGs1eRA/BQTIWumEsbCkjJyvQhu/ly7GOcn1z1RrkJuYO3JH95iqdfa63j1zHsOpo5xe7PXgbryUHOxJGCkMEbObV3Iat8vKPi2e3V//+mH3+9OvHw2iXg6NddkV7OTjay65orwZHe9UV7fXgaK+7or0ZHO1NV7S3g6O97Yr2bnC0d13R3g+O9r4r2ofB0T50LrnDt4PLtn5QQSnNyY6eNXZ8PfmT9g4PHUh+wWU1cwqtIDb82hQQumpwsDVpnP0VAAD//2DzKvg=" } diff --git a/metricbeat/module/linux/iostat/_meta/data.json b/metricbeat/module/linux/iostat/_meta/data.json new file mode 100644 index 00000000000..99f23d8cc95 --- /dev/null +++ b/metricbeat/module/linux/iostat/_meta/data.json @@ -0,0 +1,49 @@ +{ + "@timestamp": "2017-10-12T08:05:34.853Z", + "event": { + "dataset": "linux.iostat", + "duration": 115000, + "module": "linux" + }, + "linux": { + "iostat": { + "await": 0, + "busy": 0, + "name": "sr0", + "queue": { + "avg_size": 0 + }, + "read": { + "await": 0, + "per_sec": { + "bytes": 0 + }, + "request": { + "merges_per_sec": 0, + "per_sec": 0 + } + }, + "request": { + "avg_size": 0 + }, + "service_time": 0, + "write": { + "await": 0, + "per_sec": { + "bytes": 0 + }, + "request": { + "merges_per_sec": 0, + "per_sec": 0 + } + } + } + }, + "metricset": { + "name": "iostat", + "period": 10000 + }, + "service": { + "type": "linux" + } +} \ No newline at end of file diff --git a/metricbeat/module/linux/iostat/_meta/docs.asciidoc b/metricbeat/module/linux/iostat/_meta/docs.asciidoc new file mode 100644 index 00000000000..ec4c4de1618 --- /dev/null +++ b/metricbeat/module/linux/iostat/_meta/docs.asciidoc @@ -0,0 +1,3 @@ +The iostat module reports per-disk IO statistics that emulate `iostat -x` on linux. + +NOTE: as of now, this data is part of system/diskio on Metricbeat, but can only be found in the Linux integration in Fleet. In the future, this data will be removed from system/memory. \ No newline at end of file diff --git a/metricbeat/module/linux/iostat/_meta/fields.yml b/metricbeat/module/linux/iostat/_meta/fields.yml new file mode 100644 index 00000000000..a7ded63f8fc --- /dev/null +++ b/metricbeat/module/linux/iostat/_meta/fields.yml @@ -0,0 +1,61 @@ +- name: iostat + type: group + release: beta + description: > + iostat + fields: + - name: read.request.merges_per_sec + type: float + description: > + The number of read requests merged per second that were queued to the device. + - name: write.request.merges_per_sec + type: float + description: > + The number of write requests merged per second that were queued to the device. + - name: read.request.per_sec + type: float + description: > + The number of read requests that were issued to the device per second + - name: write.request.per_sec + type: float + description: > + The number of write requests that were issued to the device per second + - name: read.per_sec.bytes + type: float + description: > + The number of Bytes read from the device per second. + format: bytes + - name: read.await + type: float + description: > + The average time spent for read requests issued to the device to be served. + - name: write.per_sec.bytes + type: float + description: > + The number of Bytes write from the device per second. + format: bytes + - name: write.await + type: float + description: > + The average time spent for write requests issued to the device to be served. + - name: request.avg_size + type: float + description: > + The average size (in bytes) of the requests that were issued to the device. + - name: queue.avg_size + type: float + description: > + The average queue length of the requests that were issued to the device. + - name: await + type: float + description: > + The average time spent for requests issued to the device to be served. + - name: service_time + type: float + description: > + The average service time (in milliseconds) for I/O requests that were issued to the device. + - name: busy + type: float + description: > + Percentage of CPU time during which I/O requests were issued to the device (bandwidth utilization for the device). Device saturation occurs when this value is close to 100%. + diff --git a/metricbeat/module/linux/iostat/data.go b/metricbeat/module/linux/iostat/data.go new file mode 100644 index 00000000000..4e546deaa34 --- /dev/null +++ b/metricbeat/module/linux/iostat/data.go @@ -0,0 +1,58 @@ +// 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 iostat + +import ( + "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/metric/system/diskio" +) + +// AddLinuxIOStat adds the linux iostat data to the provided map +func AddLinuxIOStat(extraMetrics diskio.IOMetric) common.MapStr { + return common.MapStr{ + "read": common.MapStr{ + "request": common.MapStr{ + "merges_per_sec": extraMetrics.ReadRequestMergeCountPerSec, + "per_sec": extraMetrics.ReadRequestCountPerSec, + }, + "per_sec": common.MapStr{ + "bytes": extraMetrics.ReadBytesPerSec, + }, + "await": extraMetrics.AvgReadAwaitTime, + }, + "write": common.MapStr{ + "request": common.MapStr{ + "merges_per_sec": extraMetrics.WriteRequestMergeCountPerSec, + "per_sec": extraMetrics.WriteRequestCountPerSec, + }, + "per_sec": common.MapStr{ + "bytes": extraMetrics.WriteBytesPerSec, + }, + "await": extraMetrics.AvgWriteAwaitTime, + }, + "queue": common.MapStr{ + "avg_size": extraMetrics.AvgQueueSize, + }, + "request": common.MapStr{ + "avg_size": extraMetrics.AvgRequestSize, + }, + "await": extraMetrics.AvgAwaitTime, + "service_time": extraMetrics.AvgServiceTime, + "busy": extraMetrics.BusyPct, + } +} diff --git a/metricbeat/module/linux/iostat/iostat.go b/metricbeat/module/linux/iostat/iostat.go new file mode 100644 index 00000000000..049b4daadf7 --- /dev/null +++ b/metricbeat/module/linux/iostat/iostat.go @@ -0,0 +1,104 @@ +// 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 iostat + +import ( + "github.com/pkg/errors" + + "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/common/cfgwarn" + "github.com/elastic/beats/v7/libbeat/metric/system/diskio" + "github.com/elastic/beats/v7/metricbeat/mb" +) + +// init registers the MetricSet with the central registry as soon as the program +// starts. The New function will be called later to instantiate an instance of +// the MetricSet for each host defined in the module's configuration. After the +// MetricSet has been created then Fetch will begin to be called periodically. +func init() { + mb.Registry.MustAddMetricSet("linux", "iostat", New) +} + +// MetricSet holds any configuration or state information. It must implement +// the mb.MetricSet interface. And this is best achieved by embedding +// mb.BaseMetricSet because it implements all of the required mb.MetricSet +// interface methods except for Fetch. +type MetricSet struct { + mb.BaseMetricSet + stats *diskio.IOStat + includeDevices []string +} + +// New creates a new instance of the MetricSet. New is responsible for unpacking +// any MetricSet specific configuration options if there are any. +func New(base mb.BaseMetricSet) (mb.MetricSet, error) { + cfgwarn.Beta("The linux iostat metricset is beta.") + + config := struct { + IncludeDevices []string `config:"iostat.include_devices"` + }{IncludeDevices: []string{}} + if err := base.Module().UnpackConfig(&config); err != nil { + return nil, err + } + + return &MetricSet{ + BaseMetricSet: base, + includeDevices: config.IncludeDevices, + stats: diskio.NewDiskIOStat(), + }, nil +} + +// Fetch methods implements the data gathering and data conversion to the right +// format. It publishes the event which is then forwarded to the output. In case +// of an error set the Error field of mb.Event or simply call report.Error(). +func (m *MetricSet) Fetch(report mb.ReporterV2) error { + + IOstats, err := diskio.IOCounters(m.includeDevices...) + if err != nil { + return errors.Wrap(err, "disk io counters") + } + + // Sample the current cpu counter + m.stats.OpenSampling() + + // Store the last cpu counter when finished + defer m.stats.CloseSampling() + + for _, counters := range IOstats { + event := common.MapStr{ + "name": counters.Name, + } + if counters.SerialNumber != "" { + event["serial_number"] = counters.SerialNumber + } + result, err := m.stats.CalcIOStatistics(counters) + if err != nil { + return errors.Wrap(err, "error calculating iostat") + } + IOstats := AddLinuxIOStat(result) + event.DeepUpdate(IOstats) + + isOpen := report.Event(mb.Event{ + MetricSetFields: event, + }) + if !isOpen { + return nil + } + } + return nil +} diff --git a/metricbeat/module/linux/iostat/iostat_test.go b/metricbeat/module/linux/iostat/iostat_test.go new file mode 100644 index 00000000000..6a607fc66d4 --- /dev/null +++ b/metricbeat/module/linux/iostat/iostat_test.go @@ -0,0 +1,55 @@ +// 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. + +// +build linux + +package iostat + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" +) + +func TestFetch(t *testing.T) { + f := mbtest.NewReportingMetricSetV2Error(t, getConfig()) + events, errs := mbtest.ReportingFetchV2Error(f) + + assert.Empty(t, errs) + if !assert.NotEmpty(t, events) { + t.FailNow() + } + t.Logf("%s/%s event: %+v", f.Module().Name(), f.Name(), + events[0].BeatEvent("linux", "iostat").Fields.StringToPrint()) +} + +func TestData(t *testing.T) { + f := mbtest.NewReportingMetricSetV2Error(t, getConfig()) + err := mbtest.WriteEventsReporterV2Error(f, t, ".") + if err != nil { + t.Fatal("write", err) + } +} + +func getConfig() map[string]interface{} { + return map[string]interface{}{ + "module": "linux", + "metricsets": []string{"iostat"}, + } +} diff --git a/metricbeat/module/linux/memory/_meta/data.json b/metricbeat/module/linux/memory/_meta/data.json new file mode 100644 index 00000000000..79a3d431869 --- /dev/null +++ b/metricbeat/module/linux/memory/_meta/data.json @@ -0,0 +1,59 @@ +{ + "@timestamp": "2017-10-12T08:05:34.853Z", + "event": { + "dataset": "linux.memory", + "duration": 115000, + "module": "linux" + }, + "linux": { + "memory": { + "hugepages": { + "default_size": 2097152, + "free": 0, + "reserved": 0, + "surplus": 0, + "swap": { + "out": { + "fallback": 0, + "pages": 0 + } + }, + "total": 0, + "used": { + "bytes": 0, + "pct": 0 + } + }, + "page_stats": { + "direct_efficiency": { + "pct": 0.9228 + }, + "kswapd_efficiency": { + "pct": 0.7523 + }, + "pgfree": { + "pages": 16061818710 + }, + "pgscan_direct": { + "pages": 1198580 + }, + "pgscan_kswapd": { + "pages": 50222460 + }, + "pgsteal_direct": { + "pages": 1106083 + }, + "pgsteal_kswapd": { + "pages": 37782783 + } + } + } + }, + "metricset": { + "name": "memory", + "period": 10000 + }, + "service": { + "type": "linux" + } +} \ No newline at end of file diff --git a/metricbeat/module/linux/memory/_meta/docs.asciidoc b/metricbeat/module/linux/memory/_meta/docs.asciidoc new file mode 100644 index 00000000000..8b0bbaed750 --- /dev/null +++ b/metricbeat/module/linux/memory/_meta/docs.asciidoc @@ -0,0 +1,3 @@ +The memory metricset extends system/memory and adds linux-specific memory metrics, including Huge Pages and overall paging statistics. + +NOTE: as of now, this data is part of system/memory on Metricbeat, but can only be found in the Linux integration in Fleet. In the future, this data will be removed from system/memory. \ No newline at end of file diff --git a/metricbeat/module/linux/memory/_meta/fields.yml b/metricbeat/module/linux/memory/_meta/fields.yml new file mode 100644 index 00000000000..2490e69aa21 --- /dev/null +++ b/metricbeat/module/linux/memory/_meta/fields.yml @@ -0,0 +1,78 @@ +- name: memory + type: group + release: beta + description: > + Linux memory data + fields: + - name: page_stats + type: group + description: memory page statistics + fields: + - name: pgscan_kswapd.pages + type: long + format: number + description: pages scanned by kswapd + - name: pgscan_direct.pages + type: long + format: number + description: pages scanned directly + - name: pgfree.pages + type: long + format: number + description: pages freed by the system + - name: pgsteal_kswapd.pages + type: long + format: number + description: number of pages reclaimed by kswapd + - name: pgsteal_direct.pages + type: long + format: number + description: number of pages reclaimed directly + - name: direct_efficiency.pct + type: scaled_float + format: percent + description: direct reclaim efficiency percentage. A lower percentage indicates the system is struggling to reclaim memory. + - name: kswapd_efficiency.pct + type: scaled_float + format: percent + description: kswapd reclaim efficiency percentage. A lower percentage indicates the system is struggling to reclaim memory. + - name: hugepages + type: group + prefix: "[float]" + description: This group contains statistics related to huge pages usage on the system. + fields: + - name: total + type: long + format: number + description: > + Number of huge pages in the pool. + - name: used.bytes + type: long + format: bytes + description: > + Memory used in allocated huge pages. + - name: used.pct + type: long + format: percent + description: > + Percentage of huge pages used. + - name: free + type: long + format: number + description: > + Number of available huge pages in the pool. + - name: reserved + type: long + format: number + description: > + Number of reserved but not allocated huge pages in the pool. + - name: surplus + type: long + format: number + description: > + Number of overcommited huge pages. + - name: default_size + type: long + format: bytes + description: > + Default size for huge pages. diff --git a/metricbeat/module/linux/memory/data.go b/metricbeat/module/linux/memory/data.go new file mode 100644 index 00000000000..d3f1a5ef2f8 --- /dev/null +++ b/metricbeat/module/linux/memory/data.go @@ -0,0 +1,99 @@ +// 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. + +// +build darwin freebsd linux openbsd windows + +package memory + +import ( + "github.com/pkg/errors" + + "github.com/elastic/beats/v7/libbeat/common" + mem "github.com/elastic/beats/v7/libbeat/metric/system/memory" +) + +// FetchLinuxMemStats gets page_stat and huge pages data for linux +func FetchLinuxMemStats(baseMap common.MapStr) error { + + vmstat, err := mem.GetVMStat() + if err != nil { + return errors.Wrap(err, "VMStat") + } + + if vmstat != nil { + pageStats := common.MapStr{ + "pgscan_kswapd": common.MapStr{ + "pages": vmstat.PgscanKswapd, + }, + "pgscan_direct": common.MapStr{ + "pages": vmstat.PgscanDirect, + }, + "pgfree": common.MapStr{ + "pages": vmstat.Pgfree, + }, + "pgsteal_kswapd": common.MapStr{ + "pages": vmstat.PgstealKswapd, + }, + "pgsteal_direct": common.MapStr{ + "pages": vmstat.PgstealDirect, + }, + } + // This is similar to the vmeff stat gathered by sar + // these ratios calculate thhe efficiency of page reclaim + if vmstat.PgscanDirect != 0 { + pageStats["direct_efficiency"] = common.MapStr{ + "pct": common.Round(float64(vmstat.PgstealDirect)/float64(vmstat.PgscanDirect), common.DefaultDecimalPlacesCount), + } + } + + if vmstat.PgscanKswapd != 0 { + pageStats["kswapd_efficiency"] = common.MapStr{ + "pct": common.Round(float64(vmstat.PgstealKswapd)/float64(vmstat.PgscanKswapd), common.DefaultDecimalPlacesCount), + } + } + baseMap["page_stats"] = pageStats + } + + hugePagesStat, err := mem.GetHugeTLBPages() + if err != nil { + return errors.Wrap(err, "hugepages") + } + if hugePagesStat != nil { + mem.AddHugeTLBPagesPercentage(hugePagesStat) + thp := common.MapStr{ + "total": hugePagesStat.Total, + "used": common.MapStr{ + "bytes": hugePagesStat.TotalAllocatedSize, + "pct": hugePagesStat.UsedPercent, + }, + "free": hugePagesStat.Free, + "reserved": hugePagesStat.Reserved, + "surplus": hugePagesStat.Surplus, + "default_size": hugePagesStat.DefaultSize, + } + if vmstat != nil { + thp["swap"] = common.MapStr{ + "out": common.MapStr{ + "pages": vmstat.ThpSwpout, + "fallback": vmstat.ThpSwpoutFallback, + }, + } + } + baseMap["hugepages"] = thp + } + return nil +} diff --git a/metricbeat/module/linux/memory/memory.go b/metricbeat/module/linux/memory/memory.go new file mode 100644 index 00000000000..163e3bea771 --- /dev/null +++ b/metricbeat/module/linux/memory/memory.go @@ -0,0 +1,62 @@ +// 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 memory + +import ( + "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/common/cfgwarn" + "github.com/elastic/beats/v7/metricbeat/mb" +) + +// init registers the MetricSet with the central registry as soon as the program +// starts. The New function will be called later to instantiate an instance of +// the MetricSet for each host defined in the module's configuration. After the +// MetricSet has been created then Fetch will begin to be called periodically. +func init() { + mb.Registry.MustAddMetricSet("linux", "memory", New, mb.DefaultMetricSet()) +} + +// MetricSet holds any configuration or state information. It must implement +// the mb.MetricSet interface. And this is best achieved by embedding +// mb.BaseMetricSet because it implements all of the required mb.MetricSet +// interface methods except for Fetch. +type MetricSet struct { + mb.BaseMetricSet +} + +// New creates a new instance of the MetricSet. New is responsible for unpacking +// any MetricSet specific configuration options if there are any. +func New(base mb.BaseMetricSet) (mb.MetricSet, error) { + cfgwarn.Beta("The linux memory metricset is beta.") + + return &MetricSet{ + BaseMetricSet: base, + }, nil +} + +// Fetch methods implements the data gathering and data conversion to the right +// format. It publishes the event which is then forwarded to the output. In case +// of an error set the Error field of mb.Event or simply call report.Error(). +func (m *MetricSet) Fetch(report mb.ReporterV2) error { + rootEvent := common.MapStr{} + FetchLinuxMemStats(rootEvent) + report.Event(mb.Event{ + MetricSetFields: rootEvent, + }) + return nil +} diff --git a/metricbeat/module/linux/memory/memory_test.go b/metricbeat/module/linux/memory/memory_test.go new file mode 100644 index 00000000000..2980c94841e --- /dev/null +++ b/metricbeat/module/linux/memory/memory_test.go @@ -0,0 +1,55 @@ +// 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. + +// +build darwin freebsd linux openbsd windows + +package memory + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" +) + +func TestFetch(t *testing.T) { + f := mbtest.NewReportingMetricSetV2Error(t, getConfig()) + events, errs := mbtest.ReportingFetchV2Error(f) + + assert.Empty(t, errs) + if !assert.NotEmpty(t, events) { + t.FailNow() + } + t.Logf("%s/%s event: %+v", f.Module().Name(), f.Name(), + events[0].BeatEvent("linux", "memory").Fields.StringToPrint()) +} + +func TestData(t *testing.T) { + f := mbtest.NewReportingMetricSetV2Error(t, getConfig()) + err := mbtest.WriteEventsReporterV2Error(f, t, ".") + if err != nil { + t.Fatal("write", err) + } +} + +func getConfig() map[string]interface{} { + return map[string]interface{}{ + "module": "linux", + "metricsets": []string{"memory"}, + } +} diff --git a/metricbeat/module/system/cpu/_meta/data.json b/metricbeat/module/system/cpu/_meta/data.json index 4a5fd7c8ff7..cb9548ec6e4 100644 --- a/metricbeat/module/system/cpu/_meta/data.json +++ b/metricbeat/module/system/cpu/_meta/data.json @@ -7,7 +7,7 @@ }, "host": { "cpu": { - "pct": 0.0816 + "pct": 0.1629 } }, "metricset": { @@ -19,68 +19,68 @@ }, "system": { "cpu": { - "cores": 12, + "cores": 4, "idle": { "norm": { - "pct": 0.9184 + "pct": 0.8371 }, - "pct": 11.0208, - "ticks": 1964402 + "pct": 3.3484, + "ticks": 806327077 }, "iowait": { "norm": { "pct": 0 }, "pct": 0, - "ticks": 5083 + "ticks": 312229 }, "irq": { "norm": { - "pct": 0 + "pct": 0.0125 }, - "pct": 0, - "ticks": 0 + "pct": 0.0501, + "ticks": 6548016 }, "nice": { "norm": { "pct": 0 }, "pct": 0, - "ticks": 9752 + "ticks": 517231 }, "softirq": { "norm": { - "pct": 0.0058 + "pct": 0.0025 }, - "pct": 0.0699, - "ticks": 10386 + "pct": 0.01, + "ticks": 1561223 }, "steal": { "norm": { "pct": 0 }, "pct": 0, - "ticks": 0 + "ticks": 156646 }, "system": { "norm": { - "pct": 0.005 + "pct": 0.0551 }, - "pct": 0.06, - "ticks": 22274 + "pct": 0.2206, + "ticks": 31037256 }, "total": { "norm": { - "pct": 0.0816 + "pct": 0.1629 }, - "pct": 0.9792 + "pct": 0.6516 }, "user": { "norm": { - "pct": 0.0708 + "pct": 0.0927 }, - "pct": 0.8493, - "ticks": 123767 + "pct": 0.3709, + "ticks": 139619320 } } } diff --git a/metricbeat/module/system/cpu/cpu.go b/metricbeat/module/system/cpu/cpu.go index 7333df6dec7..7650b65cd80 100644 --- a/metricbeat/module/system/cpu/cpu.go +++ b/metricbeat/module/system/cpu/cpu.go @@ -20,11 +20,8 @@ package cpu import ( - "strings" - "github.com/pkg/errors" - "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/metric/system/cpu" "github.com/elastic/beats/v7/metricbeat/mb" "github.com/elastic/beats/v7/metricbeat/mb/parse" @@ -69,50 +66,7 @@ func (m *MetricSet) Fetch(r mb.ReporterV2) error { return errors.Wrap(err, "failed to fetch CPU times") } - event := common.MapStr{"cores": cpu.NumCores} - hostFields := common.MapStr{} - for _, metric := range m.config.Metrics { - switch strings.ToLower(metric) { - case percentages: - pct := sample.Percentages() - event.Put("user.pct", pct.User) - event.Put("system.pct", pct.System) - event.Put("idle.pct", pct.Idle) - event.Put("iowait.pct", pct.IOWait) - event.Put("irq.pct", pct.IRQ) - event.Put("nice.pct", pct.Nice) - event.Put("softirq.pct", pct.SoftIRQ) - event.Put("steal.pct", pct.Steal) - event.Put("total.pct", pct.Total) - case normalizedPercentages: - normalizedPct := sample.NormalizedPercentages() - event.Put("user.norm.pct", normalizedPct.User) - event.Put("system.norm.pct", normalizedPct.System) - event.Put("idle.norm.pct", normalizedPct.Idle) - event.Put("iowait.norm.pct", normalizedPct.IOWait) - event.Put("irq.norm.pct", normalizedPct.IRQ) - event.Put("nice.norm.pct", normalizedPct.Nice) - event.Put("softirq.norm.pct", normalizedPct.SoftIRQ) - event.Put("steal.norm.pct", normalizedPct.Steal) - event.Put("total.norm.pct", normalizedPct.Total) - hostFields.Put("host.cpu.pct", normalizedPct.Total) - case ticks: - ticks := sample.Ticks() - event.Put("user.ticks", ticks.User) - event.Put("system.ticks", ticks.System) - event.Put("idle.ticks", ticks.Idle) - event.Put("iowait.ticks", ticks.IOWait) - event.Put("irq.ticks", ticks.IRQ) - event.Put("nice.ticks", ticks.Nice) - event.Put("softirq.ticks", ticks.SoftIRQ) - event.Put("steal.ticks", ticks.Steal) - } - } - - r.Event(mb.Event{ - RootFields: hostFields, - MetricSetFields: event, - }) + r.Event(collectCPUMetrics(m.config.Metrics, sample)) return nil } diff --git a/metricbeat/module/system/cpu/data.go b/metricbeat/module/system/cpu/data.go new file mode 100644 index 00000000000..497a91a6173 --- /dev/null +++ b/metricbeat/module/system/cpu/data.go @@ -0,0 +1,112 @@ +// 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. + +// +build darwin freebsd linux openbsd windows + +package cpu + +import ( + "runtime" + "strings" + + "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/metric/system/cpu" + "github.com/elastic/beats/v7/metricbeat/mb" +) + +// CPU metrics are highly OS-specific, so we need to build the event per-OS +func getPlatformCPUMetrics(sample *cpu.Metrics, selectors []string, event common.MapStr) { + for _, metric := range selectors { + switch strings.ToLower(metric) { + case percentages: + pct := sample.Percentages() + event.Put("user.pct", pct.User) + event.Put("system.pct", pct.System) + event.Put("idle.pct", pct.Idle) + event.Put("total.pct", pct.Total) + + if runtime.GOOS != "windows" { + event.Put("nice.pct", pct.Nice) + } + if runtime.GOOS == "linux" || runtime.GOOS == "openbsd" { + event.Put("irq.pct", pct.IRQ) + } + if runtime.GOOS == "linux" || runtime.GOOS == "aix" { + event.Put("iowait.pct", pct.IOWait) + } + if runtime.GOOS == "linux" { + event.Put("softirq.pct", pct.SoftIRQ) + event.Put("steal.pct", pct.Steal) + } + case normalizedPercentages: + normalizedPct := sample.NormalizedPercentages() + event.Put("user.norm.pct", normalizedPct.User) + event.Put("system.norm.pct", normalizedPct.System) + event.Put("idle.norm.pct", normalizedPct.Idle) + event.Put("total.norm.pct", normalizedPct.Total) + + if runtime.GOOS != "windows" { + event.Put("nice.norm.pct", normalizedPct.Nice) + } + if runtime.GOOS == "linux" || runtime.GOOS == "openbsd" { + event.Put("irq.norm.pct", normalizedPct.IRQ) + } + if runtime.GOOS == "linux" || runtime.GOOS == "aix" { + event.Put("iowait.norm.pct", normalizedPct.IOWait) + } + if runtime.GOOS == "linux" { + event.Put("softirq.norm.pct", normalizedPct.SoftIRQ) + event.Put("steal.norm.pct", normalizedPct.Steal) + } + case ticks: + ticks := sample.Ticks() + event.Put("user.ticks", ticks.User) + event.Put("system.ticks", ticks.System) + event.Put("idle.ticks", ticks.Idle) + + if runtime.GOOS != "windows" { + event.Put("nice.ticks", ticks.Nice) + } + if runtime.GOOS == "linux" || runtime.GOOS == "openbsd" { + event.Put("irq.ticks", ticks.IRQ) + } + if runtime.GOOS == "linux" || runtime.GOOS == "aix" { + event.Put("iowait.ticks", ticks.IOWait) + } + if runtime.GOOS == "linux" { + event.Put("softirq.ticks", ticks.SoftIRQ) + event.Put("steal.ticks", ticks.Steal) + } + } + } +} + +// gather CPU metrics +func collectCPUMetrics(selectors []string, sample *cpu.Metrics) mb.Event { + event := common.MapStr{"cores": cpu.NumCores} + getPlatformCPUMetrics(sample, selectors, event) + + //generate the host fields here, since we don't want users disabling it. + normalizedPct := sample.NormalizedPercentages() + hostFields := common.MapStr{} + hostFields.Put("host.cpu.pct", normalizedPct.Total) + + return mb.Event{ + RootFields: hostFields, + MetricSetFields: event, + } +} diff --git a/metricbeat/module/system/diskio/_meta/data.json b/metricbeat/module/system/diskio/_meta/data.json index b9c8533b0c8..a2301ef8888 100644 --- a/metricbeat/module/system/diskio/_meta/data.json +++ b/metricbeat/module/system/diskio/_meta/data.json @@ -15,7 +15,7 @@ "system": { "diskio": { "io": { - "time": 364 + "time": 601740 }, "iostat": { "await": 0, @@ -48,16 +48,16 @@ } } }, - "name": "loop1", + "name": "sdb1", "read": { - "bytes": 5267456, - "count": 4124, - "time": 557 + "bytes": 25128030208, + "count": 3146154, + "time": 833872 }, "write": { - "bytes": 0, - "count": 0, - "time": 0 + "bytes": 34401640448, + "count": 861040, + "time": 11224168 } } } diff --git a/metricbeat/module/system/diskio/diskio.go b/metricbeat/module/system/diskio/diskio.go index 9da3a3c2344..1359180cff6 100644 --- a/metricbeat/module/system/diskio/diskio.go +++ b/metricbeat/module/system/diskio/diskio.go @@ -20,9 +20,14 @@ package diskio import ( + "fmt" + "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/metric/system/diskio" "github.com/elastic/beats/v7/metricbeat/mb" "github.com/elastic/beats/v7/metricbeat/mb/parse" + "github.com/elastic/beats/v7/metricbeat/module/linux/iostat" + "github.com/elastic/beats/v7/metricbeat/module/system" "github.com/pkg/errors" ) @@ -36,9 +41,10 @@ func init() { // MetricSet for fetching system disk IO metrics. type MetricSet struct { mb.BaseMetricSet - statistics *DiskIOStat + statistics *diskio.IOStat includeDevices []string prevCounters diskCounter + IsAgent bool } // diskCounter stores previous disk counter values for calculating gauges in next collection @@ -57,17 +63,23 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { return nil, err } + systemModule, ok := base.Module().(*system.Module) + if !ok { + return nil, fmt.Errorf("unexpected module type") + } + return &MetricSet{ BaseMetricSet: base, - statistics: NewDiskIOStat(), + statistics: diskio.NewDiskIOStat(), includeDevices: config.IncludeDevices, prevCounters: diskCounter{}, + IsAgent: systemModule.IsAgent, }, nil } // Fetch fetches disk IO metrics from the OS. func (m *MetricSet) Fetch(r mb.ReporterV2) error { - stats, err := IOCounters(m.includeDevices...) + stats, err := diskio.IOCounters(m.includeDevices...) if err != nil { return errors.Wrap(err, "disk io counters") } @@ -101,40 +113,13 @@ func (m *MetricSet) Fetch(r mb.ReporterV2) error { diskReadBytes += counters.ReadBytes diskWriteBytes += counters.WriteBytes - var extraMetrics DiskIOMetric - err := m.statistics.CalIOStatistics(&extraMetrics, counters) - if err == nil { - event["iostat"] = common.MapStr{ - "read": common.MapStr{ - "request": common.MapStr{ - "merges_per_sec": extraMetrics.ReadRequestMergeCountPerSec, - "per_sec": extraMetrics.ReadRequestCountPerSec, - }, - "per_sec": common.MapStr{ - "bytes": extraMetrics.ReadBytesPerSec, - }, - "await": extraMetrics.AvgReadAwaitTime, - }, - "write": common.MapStr{ - "request": common.MapStr{ - "merges_per_sec": extraMetrics.WriteRequestMergeCountPerSec, - "per_sec": extraMetrics.WriteRequestCountPerSec, - }, - "per_sec": common.MapStr{ - "bytes": extraMetrics.WriteBytesPerSec, - }, - "await": extraMetrics.AvgWriteAwaitTime, - }, - "queue": common.MapStr{ - "avg_size": extraMetrics.AvgQueueSize, - }, - "request": common.MapStr{ - "avg_size": extraMetrics.AvgRequestSize, - }, - "await": extraMetrics.AvgAwaitTime, - "service_time": extraMetrics.AvgServiceTime, - "busy": extraMetrics.BusyPct, + //Add linux-only data if agent is off as not to make breaking changes. + if !m.IsAgent { + result, err := m.statistics.CalcIOStatistics(counters) + if err != nil { + return errors.Wrap(err, "error calculating iostat") } + event["iostat"] = iostat.AddLinuxIOStat(result) } if counters.SerialNumber != "" { diff --git a/metricbeat/module/system/diskio/diskio_test.go b/metricbeat/module/system/diskio/diskio_test.go index aabc44c374b..b5494ac4bfe 100644 --- a/metricbeat/module/system/diskio/diskio_test.go +++ b/metricbeat/module/system/diskio/diskio_test.go @@ -27,8 +27,48 @@ import ( "github.com/stretchr/testify/assert" mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + "github.com/elastic/beats/v7/metricbeat/module/system" ) +func TestDataNameFilter(t *testing.T) { + oldFS := system.HostFS + newFS := "_meta/testdata" + system.HostFS = &newFS + defer func() { + system.HostFS = oldFS + }() + + conf := map[string]interface{}{ + "module": "system", + "metricsets": []string{"diskio"}, + "diskio.include_devices": []string{"sda", "sda1", "sda2"}, + } + + f := mbtest.NewReportingMetricSetV2Error(t, conf) + data, errs := mbtest.ReportingFetchV2Error(f) + assert.Empty(t, errs) + assert.Equal(t, 3, len(data)) +} + +func TestDataEmptyFilter(t *testing.T) { + oldFS := system.HostFS + newFS := "_meta/testdata" + system.HostFS = &newFS + defer func() { + system.HostFS = oldFS + }() + + conf := map[string]interface{}{ + "module": "system", + "metricsets": []string{"diskio"}, + } + + f := mbtest.NewReportingMetricSetV2Error(t, conf) + data, errs := mbtest.ReportingFetchV2Error(f) + assert.Empty(t, errs) + assert.Equal(t, 10, len(data)) +} + func TestFetch(t *testing.T) { f := mbtest.NewReportingMetricSetV2Error(t, getConfig()) events, errs := mbtest.ReportingFetchV2Error(f) diff --git a/metricbeat/module/system/fields.go b/metricbeat/module/system/fields.go index 28135f5fa95..d78002d2847 100644 --- a/metricbeat/module/system/fields.go +++ b/metricbeat/module/system/fields.go @@ -32,5 +32,5 @@ func init() { // AssetSystem returns asset data. // This is the base64 encoded gzipped contents of module/system. func AssetSystem() string { - return "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" + return "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" } diff --git a/metricbeat/module/system/filesystem/_meta/data.json b/metricbeat/module/system/filesystem/_meta/data.json index 2cb9f0897ac..4f13fff0465 100644 --- a/metricbeat/module/system/filesystem/_meta/data.json +++ b/metricbeat/module/system/filesystem/_meta/data.json @@ -1,33 +1,30 @@ { "@timestamp": "2017-10-12T08:05:34.853Z", - "agent": { - "hostname": "host.example.com", - "name": "host.example.com" - }, "event": { "dataset": "system.filesystem", "duration": 115000, "module": "system" }, "metricset": { - "name": "filesystem" + "name": "filesystem", + "period": 10000 }, "service": { "type": "system" }, "system": { "filesystem": { - "available": 53067177984, - "device_name": "/dev/dm-5", - "files": 0, - "free": 54342492160, - "free_files": 0, - "mount_point": "/var/lib/lxd/storage-pools/default", - "total": 86301999104, - "type": "btrfs", + "available": 148708327424, + "device_name": "/dev/mapper/fedora-root", + "files": 105089024, + "free": 148708327424, + "free_files": 103974920, + "mount_point": "/", + "total": 215211835392, + "type": "xfs", "used": { - "bytes": 31959506944, - "pct": 0.3759 + "bytes": 66503507968, + "pct": 0.309 } } } diff --git a/metricbeat/module/system/filesystem/helper.go b/metricbeat/module/system/filesystem/helper.go index d64ef95f562..4d53ddf2a79 100644 --- a/metricbeat/module/system/filesystem/helper.go +++ b/metricbeat/module/system/filesystem/helper.go @@ -145,20 +145,23 @@ func AddFileSystemUsedPercentage(f *FSStat) { // GetFilesystemEvent turns a stat struct into a MapStr func GetFilesystemEvent(fsStat *FSStat) common.MapStr { - return common.MapStr{ + evt := common.MapStr{ "type": fsStat.SysTypeName, "device_name": fsStat.DevName, "mount_point": fsStat.Mount, "total": fsStat.Total, - "free": fsStat.Free, "available": fsStat.Avail, "files": fsStat.Files, - "free_files": fsStat.FreeFiles, "used": common.MapStr{ "pct": fsStat.UsedPercent, "bytes": fsStat.Used, }, } + if runtime.GOOS != "windows" { + evt.Put("free", fsStat.Free) + evt.Put("free_files", fsStat.FreeFiles) + } + return evt } // Predicate is a function predicate for use with filesystems. It returns true diff --git a/metricbeat/module/system/fsstat/_meta/fields.yml b/metricbeat/module/system/fsstat/_meta/fields.yml index d9d2b0e7ce1..d9bd3acd7f3 100644 --- a/metricbeat/module/system/fsstat/_meta/fields.yml +++ b/metricbeat/module/system/fsstat/_meta/fields.yml @@ -10,7 +10,7 @@ description: Number of file systems found. - name: total_files type: long - description: Total number of files. + description: Total number of files. Not on Windows. - name: total_size format: bytes type: group diff --git a/metricbeat/module/system/fsstat/fsstat.go b/metricbeat/module/system/fsstat/fsstat.go index a8a1eaa0c94..0f43cf4af55 100644 --- a/metricbeat/module/system/fsstat/fsstat.go +++ b/metricbeat/module/system/fsstat/fsstat.go @@ -20,6 +20,7 @@ package fsstat import ( + "runtime" "strings" "github.com/elastic/beats/v7/libbeat/common" @@ -91,16 +92,23 @@ func (m *MetricSet) Fetch(r mb.ReporterV2) error { totalSizeUsed += stat.Used } - r.Event(mb.Event{ - MetricSetFields: common.MapStr{ - "total_size": common.MapStr{ - "free": totalSizeFree, - "used": totalSizeUsed, - "total": totalSize, - }, - "count": len(fss), - "total_files": totalFiles, + event := common.MapStr{ + "total_size": common.MapStr{ + "free": totalSizeFree, + "used": totalSizeUsed, + "total": totalSize, }, + "count": len(fss), + "total_files": totalFiles, + } + + //We don't get the `Files` field on Windows + if runtime.GOOS == "windows" { + event["total_files"] = totalFiles + } + + r.Event(mb.Event{ + MetricSetFields: event, }) return nil diff --git a/metricbeat/module/system/memory/_meta/data.json b/metricbeat/module/system/memory/_meta/data.json index 01231bede4c..06abee40a35 100644 --- a/metricbeat/module/system/memory/_meta/data.json +++ b/metricbeat/module/system/memory/_meta/data.json @@ -15,13 +15,13 @@ "system": { "memory": { "actual": { - "free": 14247317504, + "free": 8461623296, "used": { - "bytes": 1407057920, - "pct": 0.0899 + "bytes": 7159164928, + "pct": 0.4583 } }, - "free": 4859097088, + "free": 1299234816, "hugepages": { "default_size": 2097152, "free": 0, @@ -41,49 +41,49 @@ }, "page_stats": { "direct_efficiency": { - "pct": 0.9976 + "pct": 0.9242 }, "kswapd_efficiency": { - "pct": 0.6213 + "pct": 0.7518 }, "pgfree": { - "pages": 4382105954 + "pages": 15924304810 }, "pgscan_direct": { - "pages": 485820 + "pages": 1185751 }, "pgscan_kswapd": { - "pages": 77390925 + "pages": 50008148 }, "pgsteal_direct": { - "pages": 484631 + "pages": 1095884 }, "pgsteal_kswapd": { - "pages": 48081976 + "pages": 37594071 } }, "swap": { - "free": 7846490112, + "free": 7823421440, "in": { - "pages": 1111 + "pages": 2702 }, "out": { - "pages": 20255 + "pages": 23582 }, "readahead": { - "cached": 28, - "pages": 65 + "cached": 554, + "pages": 986 }, "total": 7897870336, "used": { - "bytes": 51380224, - "pct": 0.0065 + "bytes": 74448896, + "pct": 0.0094 } }, - "total": 15654375424, + "total": 15620788224, "used": { - "bytes": 10795278336, - "pct": 0.6896 + "bytes": 14321553408, + "pct": 0.9168 } } } diff --git a/metricbeat/module/system/memory/memory.go b/metricbeat/module/system/memory/memory.go index 90283f801be..27e76b85489 100644 --- a/metricbeat/module/system/memory/memory.go +++ b/metricbeat/module/system/memory/memory.go @@ -20,12 +20,16 @@ package memory import ( + "fmt" + + "github.com/pkg/errors" + "github.com/elastic/beats/v7/libbeat/common" mem "github.com/elastic/beats/v7/libbeat/metric/system/memory" "github.com/elastic/beats/v7/metricbeat/mb" "github.com/elastic/beats/v7/metricbeat/mb/parse" - - "github.com/pkg/errors" + linux "github.com/elastic/beats/v7/metricbeat/module/linux/memory" + "github.com/elastic/beats/v7/metricbeat/module/system" ) func init() { @@ -38,11 +42,18 @@ func init() { // MetricSet for fetching system memory metrics. type MetricSet struct { mb.BaseMetricSet + IsFleet bool } // New is a mb.MetricSetFactory that returns a memory.MetricSet. func New(base mb.BaseMetricSet) (mb.MetricSet, error) { - return &MetricSet{base}, nil + + systemModule, ok := base.Module().(*system.Module) + if !ok { + return nil, fmt.Errorf("unexpected module type") + } + + return &MetricSet{BaseMetricSet: base, IsFleet: systemModule.IsAgent}, nil } // Fetch fetches memory metrics from the OS. @@ -103,70 +114,18 @@ func (m *MetricSet) Fetch(r mb.ReporterV2) error { "pages": vmstat.SwapRa, "cached": vmstat.SwapRaHit, } - pageStats := common.MapStr{ - "pgscan_kswapd": common.MapStr{ - "pages": vmstat.PgscanKswapd, - }, - "pgscan_direct": common.MapStr{ - "pages": vmstat.PgscanDirect, - }, - "pgfree": common.MapStr{ - "pages": vmstat.Pgfree, - }, - "pgsteal_kswapd": common.MapStr{ - "pages": vmstat.PgstealKswapd, - }, - "pgsteal_direct": common.MapStr{ - "pages": vmstat.PgstealDirect, - }, - } - // This is similar to the vmeff stat gathered by sar - // these ratios calculate thhe efficiency of page reclaim - if vmstat.PgscanDirect != 0 { - pageStats["direct_efficiency"] = common.MapStr{ - "pct": common.Round(float64(vmstat.PgstealDirect)/float64(vmstat.PgscanDirect), common.DefaultDecimalPlacesCount), - } - } + } - if vmstat.PgscanKswapd != 0 { - pageStats["kswapd_efficiency"] = common.MapStr{ - "pct": common.Round(float64(vmstat.PgstealKswapd)/float64(vmstat.PgscanKswapd), common.DefaultDecimalPlacesCount), - } + // for backwards compatibility, only report if we're not in fleet mode + if !m.IsFleet { + err := linux.FetchLinuxMemStats(memory) + if err != nil { + return errors.Wrap(err, "error getting page stats") } - - memory["page_stats"] = pageStats } memory["swap"] = swap - hugePagesStat, err := mem.GetHugeTLBPages() - if err != nil { - return errors.Wrap(err, "hugepages") - } - if hugePagesStat != nil { - mem.AddHugeTLBPagesPercentage(hugePagesStat) - thp := common.MapStr{ - "total": hugePagesStat.Total, - "used": common.MapStr{ - "bytes": hugePagesStat.TotalAllocatedSize, - "pct": hugePagesStat.UsedPercent, - }, - "free": hugePagesStat.Free, - "reserved": hugePagesStat.Reserved, - "surplus": hugePagesStat.Surplus, - "default_size": hugePagesStat.DefaultSize, - } - if vmstat != nil { - thp["swap"] = common.MapStr{ - "out": common.MapStr{ - "pages": vmstat.ThpSwpout, - "fallback": vmstat.ThpSwpoutFallback, - }, - } - } - memory["hugepages"] = thp - } - r.Event(mb.Event{ MetricSetFields: memory, }) diff --git a/metricbeat/module/system/process/_meta/data.json b/metricbeat/module/system/process/_meta/data.json index 78ece4aa114..84b74110683 100644 --- a/metricbeat/module/system/process/_meta/data.json +++ b/metricbeat/module/system/process/_meta/data.json @@ -1,29 +1,26 @@ { "@timestamp": "2017-10-12T08:05:34.853Z", - "agent": { - "hostname": "host.example.com", - "name": "host.example.com" - }, "event": { "dataset": "system.process", "duration": 115000, "module": "system" }, "metricset": { - "name": "process" + "name": "process", + "period": 10000 }, "process": { "args": [ - "/usr/bin/dockerd", - "-H", - "unix://" + "/usr/lib/systemd/systemd", + "--switched-root", + "--system", + "--deserialize", + "28" ], - "executable": "/usr/bin/dockerd-ce", - "name": "dockerd", - "pgid": 2080, - "pid": 2080, - "ppid": 1, - "working_directory": "/" + "name": "systemd", + "pgid": 1, + "pid": 1, + "ppid": 0 }, "service": { "type": "system" @@ -32,11 +29,11 @@ "process": { "cgroup": { "blkio": { - "id": "docker.service", - "path": "/system.slice/docker.service", + "id": "init.scope", + "path": "/init.scope", "total": { - "bytes": 844576104448, - "ios": 54869430 + "bytes": 7453696, + "ios": 548 } }, "cpu": { @@ -49,8 +46,8 @@ }, "shares": 1024 }, - "id": "docker.service", - "path": "/system.slice/docker.service", + "id": "init.scope", + "path": "/init.scope", "rt": { "period": { "us": 0 @@ -68,38 +65,38 @@ } }, "cpuacct": { - "id": "docker.service", - "path": "/system.slice/docker.service", + "id": "init.scope", + "path": "/init.scope", "percpu": { - "1": 7058282754012, - "2": 7053634662537, - "3": 7069386293853, - "4": 7050055153087 + "1": 3930656993407, + "2": 4025787490535, + "3": 4064460082910, + "4": 3387847262532 }, "stats": { "system": { - "ns": 7202920000000 + "ns": 4996000000000 }, "user": { - "ns": 19573240000000 + "ns": 10329380000000 } }, "total": { - "ns": 28231358863489 + "ns": 15408751829384 } }, - "id": "docker.service", + "id": "init.scope", "memory": { - "id": "docker.service", + "id": "init.scope", "kmem": { "failures": 0, "limit": { "bytes": 9223372036854771712 }, "usage": { - "bytes": 21139456, + "bytes": 9404416, "max": { - "bytes": 480030720 + "bytes": 14987264 } } }, @@ -121,95 +118,88 @@ "bytes": 9223372036854771712 }, "usage": { - "bytes": 3337703424, + "bytes": 29437952, "max": { - "bytes": 5245300736 + "bytes": 70705152 } } }, "memsw": { "failures": 0, "limit": { - "bytes": 0 + "bytes": 9223372036854771712 }, "usage": { - "bytes": 0, + "bytes": 30392320, "max": { - "bytes": 0 + "bytes": 70705152 } } }, - "path": "/system.slice/docker.service", + "path": "/init.scope", "stats": { "active_anon": { - "bytes": 779677696 + "bytes": 3444736 }, "active_file": { - "bytes": 1753378816 + "bytes": 10563584 }, "cache": { - "bytes": 2127810560 + "bytes": 10752000 }, "hierarchical_memory_limit": { "bytes": 9223372036854771712 }, "hierarchical_memsw_limit": { - "bytes": 0 + "bytes": 9223372036854771712 }, "inactive_anon": { - "bytes": 409075712 + "bytes": 6197248 }, "inactive_file": { - "bytes": 374431744 + "bytes": 327680 }, - "major_page_faults": 53164, + "major_page_faults": 198, "mapped_file": { - "bytes": 7491584 + "bytes": 9867264 }, - "page_faults": 21923702, - "pages_in": 57261049, - "pages_out": 56521859, + "page_faults": 3626304, + "pages_in": 1095732, + "pages_out": 1090806, "rss": { - "bytes": 1188753408 + "bytes": 9592832 }, "rss_huge": { - "bytes": 56623104 + "bytes": 0 }, "swap": { - "bytes": 0 + "bytes": 675840 }, "unevictable": { "bytes": 0 } } }, - "path": "/system.slice/docker.service" + "path": "/init.scope" }, - "cmdline": "/usr/bin/dockerd -H unix://", + "cmdline": "/usr/lib/systemd/systemd --switched-root --system --deserialize 28", "cpu": { - "start_time": "2019-01-08T17:06:39.000Z", + "start_time": "2020-08-27T01:05:16.000Z", "total": { "norm": { - "pct": 0.0049 + "pct": 0.0056 }, - "pct": 0.0195, - "value": 27827810 + "pct": 0.0222, + "value": 15389060 } }, - "fd": { - "limit": { - "hard": 1048576, - "soft": 1048576 - }, - "open": 68 - }, "memory": { "rss": { - "bytes": 1187336192, - "pct": 0.0716 + "bytes": 12853248, + "pct": 0.0008 }, - "share": 8253440, - "size": 4438523904 + "share": 7118848, + "size": 176881664 }, "state": "sleeping" } @@ -217,4 +207,4 @@ "user": { "name": "root" } -} +} \ No newline at end of file diff --git a/metricbeat/module/system/process/_meta/fields.yml b/metricbeat/module/system/process/_meta/fields.yml index 51ab8b81e09..fe941031551 100644 --- a/metricbeat/module/system/process/_meta/fields.yml +++ b/metricbeat/module/system/process/_meta/fields.yml @@ -96,17 +96,27 @@ type: long format: bytes description: > - The Resident Set Size. The amount of memory the process occupied in main memory (RAM). On Windows this represents the current working set size, in bytes. + The Resident Set Size. The amount of memory the process occupied in main memory (RAM). On Windows this represents the current working set size, in bytes. Not available on Windows. - name: rss.pct type: scaled_float format: percent description: > - The percentage of memory the process occupied in main memory (RAM). + The percentage of memory the process occupied in main memory (RAM). Not available on Windows. + - name: wss.bytes + type: long + format: bytes + description: > + The Working Set Size. The amount of memory the process occupied in main memory (RAM). Windows only. + - name: wss.pct + type: scaled_float + format: percent + description: > + The percentage of memory the process occupied in main memory (RAM). Windows only. - name: share type: long format: bytes description: > - The shared memory the process uses. + The shared memory the process uses. Not available on Windows. - name: fd type: group description: > diff --git a/metricbeat/module/system/process/process.go b/metricbeat/module/system/process/process.go index 9c754177372..804c62d06d6 100644 --- a/metricbeat/module/system/process/process.go +++ b/metricbeat/module/system/process/process.go @@ -46,9 +46,10 @@ func init() { // MetricSet that fetches process metrics. type MetricSet struct { mb.BaseMetricSet - stats *process.Stats - cgroup *cgroup.Reader - perCPU bool + stats *process.Stats + cgroup *cgroup.Reader + perCPU bool + IsAgent bool } // New creates and returns a new MetricSet. @@ -58,6 +59,11 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { return nil, err } + systemModule, ok := base.Module().(*system.Module) + if !ok { + return nil, fmt.Errorf("unexpected module type") + } + m := &MetricSet{ BaseMetricSet: base, stats: &process.Stats{ @@ -67,7 +73,8 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { CacheCmdLine: config.CacheCmdLine, IncludeTop: config.IncludeTop, }, - perCPU: config.IncludePerCPU, + perCPU: config.IncludePerCPU, + IsAgent: systemModule.IsAgent, } err := m.stats.Init() if err != nil { @@ -75,11 +82,6 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { } if runtime.GOOS == "linux" { - systemModule, ok := base.Module().(*system.Module) - if !ok { - return nil, fmt.Errorf("unexpected module type") - } - if config.Cgroups == nil || *config.Cgroups { debugf("process cgroup data collection is enabled, using hostfs='%v'", systemModule.HostFS) m.cgroup, err = cgroup.NewReader(systemModule.HostFS, true) @@ -148,6 +150,22 @@ func (m *MetricSet) Fetch(r mb.ReporterV2) error { rootFields.Put("process.args", args) } + // This is a temporary fix until we make these changes global across libbeat + // This logic should happen in libbeat getProcessEvent() + + // There's some more Windows memory quirks we need to deal with. + // "rss" is a linux concept, but "wss" is a direct match on Windows. + // "share" is also unavailable on Windows. + + if m.IsAgent { + if runtime.GOOS == "windows" { + proc.Delete("memory.share") + if setSize := getAndRemove(proc, "memory.rss"); setSize != nil { + proc.Put("memory.wss", setSize) + } + } + } + e := mb.Event{ RootFields: rootFields, MetricSetFields: proc, diff --git a/metricbeat/module/system/process_summary/_meta/data.json b/metricbeat/module/system/process_summary/_meta/data.json index 363e5f77a0d..159defcb769 100644 --- a/metricbeat/module/system/process_summary/_meta/data.json +++ b/metricbeat/module/system/process_summary/_meta/data.json @@ -1,16 +1,13 @@ { "@timestamp": "2017-10-12T08:05:34.853Z", - "agent": { - "hostname": "host.example.com", - "name": "host.example.com" - }, "event": { "dataset": "system.process.summary", "duration": 115000, "module": "system" }, "metricset": { - "name": "process_summary" + "name": "process_summary", + "period": 10000 }, "service": { "type": "system" @@ -19,13 +16,13 @@ "process": { "summary": { "dead": 0, - "idle": 112, - "running": 0, - "sleeping": 288, + "idle": 63, + "running": 3, + "sleeping": 117, "stopped": 0, - "total": 401, - "unknown": 0, - "zombie": 1 + "total": 185, + "unknown": 2, + "zombie": 0 } } } diff --git a/metricbeat/module/system/process_summary/process_summary.go b/metricbeat/module/system/process_summary/process_summary.go index 071a7b2a660..5edb31f9182 100644 --- a/metricbeat/module/system/process_summary/process_summary.go +++ b/metricbeat/module/system/process_summary/process_summary.go @@ -20,6 +20,8 @@ package process_summary import ( + "runtime" + "github.com/pkg/errors" "github.com/elastic/beats/v7/libbeat/common" @@ -79,7 +81,7 @@ func (m *MetricSet) Fetch(r mb.ReporterV2) error { state := sigar.ProcState{} err = state.Get(pid) if err != nil { - summary.unknown += 1 + summary.unknown++ continue } @@ -104,15 +106,25 @@ func (m *MetricSet) Fetch(r mb.ReporterV2) error { } } - event := common.MapStr{ - "total": len(pids), - "sleeping": summary.sleeping, - "running": summary.running, - "idle": summary.idle, - "stopped": summary.stopped, - "zombie": summary.zombie, - "unknown": summary.unknown, - "dead": summary.dead, + event := common.MapStr{} + if runtime.GOOS == "windows" { + event = common.MapStr{ + "total": len(pids), + "sleeping": summary.sleeping, + "running": summary.running, + "unknown": summary.unknown, + } + } else { + event = common.MapStr{ + "total": len(pids), + "sleeping": summary.sleeping, + "running": summary.running, + "idle": summary.idle, + "stopped": summary.stopped, + "zombie": summary.zombie, + "unknown": summary.unknown, + "dead": summary.dead, + } } r.Event(mb.Event{ diff --git a/metricbeat/module/system/process_summary/process_summary_test.go b/metricbeat/module/system/process_summary/process_summary_test.go index 55afce8f190..c95e361224c 100644 --- a/metricbeat/module/system/process_summary/process_summary_test.go +++ b/metricbeat/module/system/process_summary/process_summary_test.go @@ -20,6 +20,7 @@ package process_summary import ( + "runtime" "testing" "github.com/stretchr/testify/assert" @@ -53,18 +54,26 @@ func TestFetch(t *testing.T) { event, ok := summary.(common.MapStr) require.True(t, ok) - assert.Contains(t, event, "total") - assert.Contains(t, event, "sleeping") - assert.Contains(t, event, "running") - assert.Contains(t, event, "idle") - assert.Contains(t, event, "stopped") - assert.Contains(t, event, "zombie") - assert.Contains(t, event, "unknown") + if runtime.GOOS == "windows" { + assert.Contains(t, event, "total") + assert.Contains(t, event, "sleeping") + assert.Contains(t, event, "running") + assert.Contains(t, event, "unknown") + total := event["sleeping"].(int) + event["running"].(int) + event["unknown"].(int) + assert.Equal(t, event["total"].(int), total) + } else { + assert.Contains(t, event, "total") + assert.Contains(t, event, "sleeping") + assert.Contains(t, event, "running") + assert.Contains(t, event, "idle") + assert.Contains(t, event, "stopped") + assert.Contains(t, event, "zombie") + assert.Contains(t, event, "unknown") + total := event["sleeping"].(int) + event["running"].(int) + event["idle"].(int) + + event["stopped"].(int) + event["zombie"].(int) + event["unknown"].(int) - total := event["sleeping"].(int) + event["running"].(int) + event["idle"].(int) + - event["stopped"].(int) + event["zombie"].(int) + event["unknown"].(int) - - assert.Equal(t, event["total"].(int), total) + assert.Equal(t, event["total"].(int), total) + } } func getConfig() map[string]interface{} { diff --git a/metricbeat/module/system/system.go b/metricbeat/module/system/system.go index edb9f55bf6b..f1f060a90bc 100644 --- a/metricbeat/module/system/system.go +++ b/metricbeat/module/system/system.go @@ -21,10 +21,12 @@ import ( "flag" "sync" + "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/metricbeat/mb" ) var ( + // HostFS is an alternate mountpoint for the filesytem root, for when metricbeat is running inside a container. HostFS = flag.String("system.hostfs", "", "mountpoint of the host's filesystem for use in monitoring a host from within a container") ) @@ -37,16 +39,48 @@ func init() { } } +// Module represents the system module type Module struct { mb.BaseModule - HostFS string // Mountpoint of the host's filesystem for use in monitoring inside a container. + HostFS string // Mountpoint of the host's filesystem for use in monitoring inside a container. + IsAgent bool // Looks to see if metricbeat is running under agent. Useful if we have breaking changes in one but not the other. } +// NewModule instatiates the system module func NewModule(base mb.BaseModule) (mb.Module, error) { // This only needs to be configured once for all system modules. once.Do(func() { initModule() }) - return &Module{BaseModule: base, HostFS: *HostFS}, nil + return &Module{BaseModule: base, HostFS: *HostFS, IsAgent: checkMgmtFlags()}, nil +} + +// checkMgmtFlags checks to see if metricbeat is running under Agent +// The management setting is stored in the main Beat runtime object, but we can't see that from a module +// So instead we check the CLI flags, since Agent starts metricbeat with "-E", "management.mode=x-pack-fleet", "-E", "management.enabled=true" +func checkMgmtFlags() bool { + type management struct { + Mode string `config:"management.mode"` + Enabled bool `config:"management.enabled"` + } + var managementSettings management + + cfgFlag := flag.Lookup("E") + if cfgFlag == nil { + return false + } + + CfgObject, _ := cfgFlag.Value.(*common.SettingsFlag) + cliCfg := CfgObject.Config() + + err := cliCfg.Unpack(&managementSettings) + if err != nil { + return false + } + + if managementSettings.Enabled == true && managementSettings.Mode == "x-pack-fleet" { + return true + } + return false } diff --git a/metricbeat/module/system/test_system.py b/metricbeat/module/system/test_system.py index f689b99fb4c..0cfb820c18a 100644 --- a/metricbeat/module/system/test_system.py +++ b/metricbeat/module/system/test_system.py @@ -7,13 +7,23 @@ import unittest -SYSTEM_CPU_FIELDS = ["cores", "idle.pct", "iowait.pct", "irq.pct", "nice.pct", - "softirq.pct", "steal.pct", "system.pct", "user.pct", "total.pct"] +SYSTEM_CPU_FIELDS_LINUX = ["cores", "idle.pct", "iowait.pct", "irq.pct", "nice.pct", + "softirq.pct", "steal.pct", "system.pct", "user.pct", "total.pct"] -SYSTEM_CPU_FIELDS_ALL = ["cores", "idle.pct", "idle.ticks", "iowait.pct", "iowait.ticks", "irq.pct", "irq.ticks", "nice.pct", "nice.ticks", - "softirq.pct", "softirq.ticks", "steal.pct", "steal.ticks", "system.pct", "system.ticks", "user.pct", "user.ticks", - "idle.norm.pct", "iowait.norm.pct", "irq.norm.pct", "nice.norm.pct", "softirq.norm.pct", - "steal.norm.pct", "system.norm.pct", "user.norm.pct", "total.norm.pct", "total.value"] +SYSTEM_CPU_FIELDS_WINDOWS = ["cores", "idle.pct", "system.pct", "user.pct", "total.pct"] + +SYSTEM_CPU_FIELDS_DARWIN = ["cores", "idle.pct", "system.pct", "user.pct", "total.pct", "nice.pct"] + +SYSTEM_CPU_FIELDS_LINUX_ALL = ["cores", "idle.pct", "idle.ticks", "iowait.pct", "iowait.ticks", "irq.pct", "irq.ticks", "nice.pct", "nice.ticks", + "softirq.pct", "softirq.ticks", "steal.pct", "steal.ticks", "system.pct", "system.ticks", "user.pct", "user.ticks", + "idle.norm.pct", "iowait.norm.pct", "irq.norm.pct", "nice.norm.pct", "softirq.norm.pct", + "steal.norm.pct", "system.norm.pct", "user.norm.pct", "total.norm.pct", "total.value"] + +SYSTEM_CPU_FIELDS_WINDOWS_ALL = ["cores", "idle.pct", "idle.ticks", "system.pct", "system.ticks", "user.pct", "user.ticks", + "idle.norm.pct", "system.norm.pct", "user.norm.pct", "total.norm.pct", "total.value"] + +SYSTEM_CPU_FIELDS_DARWIN_ALL = ["cores", "idle.pct", "idle.ticks", "nice.pct", "nice.ticks", "system.pct", "system.ticks", "user.pct", "user.ticks", + "idle.norm.pct", "nice.norm.pct", "system.norm.pct", "user.norm.pct", "total.norm.pct", "total.value"] SYSTEM_LOAD_FIELDS = ["cores", "1", "5", "15", "norm.1", "norm.5", "norm.15"] @@ -37,6 +47,10 @@ "free_files", "mount_point", "total", "used.bytes", "used.pct"] +SYSTEM_FILESYSTEM_FIELDS_WINDOWS = ["available", "device_name", "type", "files", + "mount_point", "total", "used.bytes", + "used.pct"] + SYSTEM_FSSTAT_FIELDS = ["count", "total_files", "total_size"] SYSTEM_MEMORY_FIELDS = ["swap", "actual.free", "free", "total", "used.bytes", "used.pct", "actual.used.bytes", @@ -82,7 +96,12 @@ def test_cpu(self): if "system" in evt: cpu = evt["system"]["cpu"] - self.assertCountEqual(self.de_dot(SYSTEM_CPU_FIELDS), cpu.keys()) + if sys.platform.startswith("linux"): + self.assertCountEqual(self.de_dot(SYSTEM_CPU_FIELDS_LINUX), cpu.keys()) + elif sys.platform.startswith("darwin"): + self.assertCountEqual(self.de_dot(SYSTEM_CPU_FIELDS_DARWIN), cpu.keys()) + elif sys.platform.startswith("win"): + self.assertCountEqual(self.de_dot(SYSTEM_CPU_FIELDS_WINDOWS), cpu.keys()) else: host_cpu = evt["host"]["cpu"] self.assertCountEqual(self.de_dot(SYSTEM_CPU_HOST_FIELDS), host_cpu.keys()) @@ -111,7 +130,12 @@ def test_cpu_ticks_option(self): for evt in output: self.assert_fields_are_documented(evt) cpuStats = evt["system"]["cpu"] - self.assertCountEqual(self.de_dot(SYSTEM_CPU_FIELDS_ALL), cpuStats.keys()) + if sys.platform.startswith("linux"): + self.assertCountEqual(self.de_dot(SYSTEM_CPU_FIELDS_LINUX_ALL), cpuStats.keys()) + elif sys.platform.startswith("win"): + self.assertCountEqual(self.de_dot(SYSTEM_CPU_FIELDS_WINDOWS_ALL), cpuStats.keys()) + elif sys.platform.startswith("darwin"): + self.assertCountEqual(self.de_dot(SYSTEM_CPU_FIELDS_DARWIN_ALL), cpuStats.keys()) @unittest.skipUnless(re.match("(?i)win|linux|darwin|freebsd|openbsd", sys.platform), "os") def test_core(self): @@ -261,7 +285,10 @@ def test_filesystem(self): for evt in output: self.assert_fields_are_documented(evt) filesystem = evt["system"]["filesystem"] - self.assertCountEqual(self.de_dot(SYSTEM_FILESYSTEM_FIELDS), filesystem.keys()) + if sys.platform.startswith("win"): + self.assertCountEqual(self.de_dot(SYSTEM_FILESYSTEM_FIELDS_WINDOWS), filesystem.keys()) + else: + self.assertCountEqual(self.de_dot(SYSTEM_FILESYSTEM_FIELDS), filesystem.keys()) @unittest.skipUnless(re.match("(?i)win|linux|darwin|freebsd|openbsd", sys.platform), "os") def test_fsstat(self): @@ -378,13 +405,17 @@ def test_process_summary(self): assert isinstance(summary["total"], int) assert isinstance(summary["sleeping"], int) assert isinstance(summary["running"], int) - assert isinstance(summary["idle"], int) - assert isinstance(summary["stopped"], int) - assert isinstance(summary["zombie"], int) assert isinstance(summary["unknown"], int) - assert summary["total"] == summary["sleeping"] + summary["running"] + \ - summary["idle"] + summary["stopped"] + summary["zombie"] + summary["unknown"] + if not sys.platform.startswith("win"): + assert isinstance(summary["idle"], int) + assert isinstance(summary["stopped"], int) + assert isinstance(summary["zombie"], int) + assert summary["total"] == summary["sleeping"] + summary["running"] + \ + summary["idle"] + summary["stopped"] + summary["zombie"] + summary["unknown"] + + if sys.platform.startswith("windows"): + assert summary["total"] == summary["sleeping"] + summary["running"] + summary["unknown"] @unittest.skipUnless(re.match("(?i)win|linux|darwin|freebsd", sys.platform), "os") def test_process(self): diff --git a/metricbeat/modules.d/linux.yml.disabled b/metricbeat/modules.d/linux.yml.disabled index a01989aa0c9..22e675cafff 100644 --- a/metricbeat/modules.d/linux.yml.disabled +++ b/metricbeat/modules.d/linux.yml.disabled @@ -5,8 +5,10 @@ period: 10s metricsets: - "pageinfo" + - "memory" # - ksm # - conntrack + # - iostat enabled: true #hostfs: /hostfs diff --git a/metricbeat/tests/system/test_processors.py b/metricbeat/tests/system/test_processors.py index 2f7d131d119..a680c049d67 100644 --- a/metricbeat/tests/system/test_processors.py +++ b/metricbeat/tests/system/test_processors.py @@ -12,13 +12,13 @@ def test_drop_fields(self): self.render_config_template( modules=[{ "name": "system", - "metricsets": ["cpu"], + "metricsets": ["network"], "period": "1s" }], processors=[{ "drop_fields": { "when": "range.system.cpu.system.pct.lt: 0.1", - "fields": ["system.cpu.load"], + "fields": ["system.network.in"], }, }] ) @@ -27,7 +27,7 @@ def test_drop_fields(self): proc.check_kill_and_wait() output = self.read_output_json() - self.assertEqual(len(output), 1) + self.assertGreater(len(output), 1) evt = output[0] self.assert_fields_are_documented(evt) @@ -37,12 +37,9 @@ def test_drop_fields(self): 'agent', '@timestamp', 'system', 'metricset.module', 'metricset.rtt', 'metricset.name', 'host', 'service', 'ecs', 'event' ]), evt.keys()) - cpu = evt["system"]["cpu"] - print(list(cpu.keys())) - self.assertCountEqual(self.de_dot([ - "system", "cores", "user", "softirq", "iowait", - "idle", "irq", "steal", "nice", "total" - ]), cpu.keys()) + network = evt["system"]["network"] + print(list(network.keys())) + self.assertCountEqual(self.de_dot(["name", "out", "in"]), network.keys()) def test_dropfields_with_condition(self): """ diff --git a/x-pack/metricbeat/metricbeat.reference.yml b/x-pack/metricbeat/metricbeat.reference.yml index 17c9234ea9f..bd99c2cf508 100644 --- a/x-pack/metricbeat/metricbeat.reference.yml +++ b/x-pack/metricbeat/metricbeat.reference.yml @@ -902,8 +902,10 @@ metricbeat.modules: period: 10s metricsets: - "pageinfo" + - "memory" # - ksm # - conntrack + # - iostat enabled: true #hostfs: /hostfs From fac0347d36205abd3e9e1ac377fdd9d263b8c9f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20Fl=C3=BChmann?= Date: Wed, 7 Oct 2020 01:56:39 +0200 Subject: [PATCH 45/93] docs: Update timestamp.asciidoc (#20395) --- libbeat/processors/timestamp/docs/timestamp.asciidoc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libbeat/processors/timestamp/docs/timestamp.asciidoc b/libbeat/processors/timestamp/docs/timestamp.asciidoc index 83421c584a7..eb5ba628995 100644 --- a/libbeat/processors/timestamp/docs/timestamp.asciidoc +++ b/libbeat/processors/timestamp/docs/timestamp.asciidoc @@ -50,7 +50,7 @@ If a layout does not contain a year then the current year in the specified Here is an example that parses the `start_time` field and writes the result to the `@timestamp` field then deletes the `start_time` field. When the -processor is loaded it will immediately validate that the two `test` timestamps +processor is loaded, it will immediately validate that the two `test` timestamps parse with this configuration. [source,yaml] @@ -61,9 +61,11 @@ processors: layouts: - '2006-01-02T15:04:05Z' - '2006-01-02T15:04:05.999Z' + - '2006-01-02T15:04:05.999-07:00' test: - '2019-06-22T16:33:51Z' - '2019-11-18T04:59:51.123Z' + - '2020-08-03T07:10:20.123456+02:00' - drop_fields: fields: [start_time] ---- From b58aeb6e4b928659572df8d70118d7e31f26e1ef Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Wed, 7 Oct 2020 12:33:19 +0200 Subject: [PATCH 46/93] Fix badger build in 386 (#21613) Use a fork with the latest released version including the fix for the issue with 32-bit architectures. Fix uses uint32 instead of 32-bit int to avoid overflow in constant with max uint32. --- NOTICE.txt | 6 +++--- go.mod | 1 + go.sum | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/NOTICE.txt b/NOTICE.txt index 0017abeba1a..b62c4b5d202 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -4473,12 +4473,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- -Dependency : github.com/dgraph-io/badger/v2 -Version: v2.2007.2 +Dependency : github.com/elastic/badger/v2 +Version: v2.2007.2-beats Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/dgraph-io/badger/v2@v2.2007.2/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/elastic/badger/v2@v2.2007.2-beats/LICENSE: Apache License Version 2.0, January 2004 diff --git a/go.mod b/go.mod index bb31374384a..0f0bdb3a422 100644 --- a/go.mod +++ b/go.mod @@ -191,6 +191,7 @@ replace ( github.com/Azure/go-autorest => github.com/Azure/go-autorest v12.2.0+incompatible github.com/Shopify/sarama => github.com/elastic/sarama v1.19.1-0.20200629123429-0e7b69039eec github.com/cucumber/godog => github.com/cucumber/godog v0.8.1 + github.com/dgraph-io/badger/v2 => github.com/elastic/badger/v2 v2.2007.2-beats github.com/docker/docker => github.com/docker/engine v0.0.0-20191113042239-ea84732a7725 github.com/docker/go-plugins-helpers => github.com/elastic/go-plugins-helpers v0.0.0-20200207104224-bdf17607b79f github.com/dop251/goja => github.com/andrewkroh/goja v0.0.0-20190128172624-dd2ac4456e20 diff --git a/go.sum b/go.sum index 8113d42f321..6f737d7cee2 100644 --- a/go.sum +++ b/go.sum @@ -201,8 +201,6 @@ github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e/go.mod h1:xb github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= github.com/devigned/tab v0.1.2-0.20190607222403-0c15cf42f9a2 h1:6+hM8KeYKV0Z9EIINNqIEDyyIRAcNc2FW+/TUYNmWyw= github.com/devigned/tab v0.1.2-0.20190607222403-0c15cf42f9a2/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= -github.com/dgraph-io/badger/v2 v2.2007.2 h1:EjjK0KqwaFMlPin1ajhP943VPENHJdEz1KLIegjaI3k= -github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de h1:t0UHb5vdojIDUqktM6+xJAfScFBsVpXZmqC9dsgJmeA= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -241,6 +239,8 @@ github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/eclipse/paho.mqtt.golang v1.2.1-0.20200121105743-0d940dd29fd2 h1:DW6WrARxK5J+o8uAKCiACi5wy9EK1UzrsCpGBPsKHAA= github.com/eclipse/paho.mqtt.golang v1.2.1-0.20200121105743-0d940dd29fd2/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= +github.com/elastic/badger/v2 v2.2007.2-beats h1:/rV4bM6fdYvPQhFf2bHHivIb0H4nX8Or7nkWbQ/Q6Ko= +github.com/elastic/badger/v2 v2.2007.2-beats/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/elastic/dhcp v0.0.0-20200227161230-57ec251c7eb3 h1:lnDkqiRFKm0rxdljqrj3lotWinO9+jFmeDXIC4gvIQs= github.com/elastic/dhcp v0.0.0-20200227161230-57ec251c7eb3/go.mod h1:aPqzac6AYkipvp4hufTyMj5PDIphF3+At8zr7r51xjY= github.com/elastic/ecs v1.6.0 h1:8NmgfnsjmKXh9hVsK3H2tZtfUptepNc3msJOAynhtmc= From 3e86c2af0594d792f62af39a0b0e6b530e9aa90d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 7 Oct 2020 13:02:34 +0200 Subject: [PATCH 47/93] [E2E Tests] fix: set versions ony for PRs (#21608) * fix: set versions ony for PRs We want to use default versions per branch when running after a merge * fix: add trailing comma Co-authored-by: Victor Martinez Co-authored-by: Victor Martinez --- .ci/packaging.groovy | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/.ci/packaging.groovy b/.ci/packaging.groovy index 301ead43bab..26916372b70 100644 --- a/.ci/packaging.groovy +++ b/.ci/packaging.groovy @@ -245,19 +245,25 @@ def triggerE2ETests(String suite) { def branchName = isPR() ? "${env.CHANGE_TARGET}" : "${env.JOB_BASE_NAME}" def e2eTestsPipeline = "e2e-tests/e2e-testing-mbp/${branchName}" + + def parameters = [ + booleanParam(name: 'forceSkipGitChecks', value: true), + booleanParam(name: 'forceSkipPresubmit', value: true), + booleanParam(name: 'notifyOnGreenBuilds', value: !isPR()), + string(name: 'runTestsSuites', value: suite), + string(name: 'GITHUB_CHECK_NAME', value: env.GITHUB_CHECK_E2E_TESTS_NAME), + string(name: 'GITHUB_CHECK_REPO', value: env.REPO), + string(name: 'GITHUB_CHECK_SHA1', value: env.GIT_BASE_COMMIT), + ] + if (isPR()) { + def version = "pr-${env.CHANGE_ID}" + parameters.push(booleanParam(name: 'USE_CI_SNAPSHOTS', value: true)) + parameters.push(string(name: 'ELASTIC_AGENT_VERSION', value: "${version}")) + parameters.push(string(name: 'METRICBEAT_VERSION', value: "${version}")) + } + build(job: "${e2eTestsPipeline}", - parameters: [ - booleanParam(name: 'forceSkipGitChecks', value: true), - booleanParam(name: 'forceSkipPresubmit', value: true), - booleanParam(name: 'notifyOnGreenBuilds', value: !isPR()), - booleanParam(name: 'USE_CI_SNAPSHOTS', value: true), - string(name: 'ELASTIC_AGENT_VERSION', value: "pr-${env.CHANGE_ID}"), - string(name: 'METRICBEAT_VERSION', value: "pr-${env.CHANGE_ID}"), - string(name: 'runTestsSuites', value: suite), - string(name: 'GITHUB_CHECK_NAME', value: env.GITHUB_CHECK_E2E_TESTS_NAME), - string(name: 'GITHUB_CHECK_REPO', value: env.REPO), - string(name: 'GITHUB_CHECK_SHA1', value: env.GIT_BASE_COMMIT), - ], + parameters: parameters, propagate: false, wait: false ) From 36953dfe6a587c0083cacb350f4d274e09d634a4 Mon Sep 17 00:00:00 2001 From: Dan Roscigno Date: Wed, 7 Oct 2020 09:59:05 -0400 Subject: [PATCH 48/93] Add `add_observer_metadata` `geo.name` to Quickstart (#21501) * Add `add_observer_metadata` `geo.name` to Quickstart The observer location is very important in the Uptime app and the out-of-the-box machine learning job. * Update getting-started.asciidoc The instructions about editing heartbeat.yml were too wordy * tighten it up! Co-authored-by: DeDe Morton Co-authored-by: DeDe Morton --- heartbeat/docs/getting-started.asciidoc | 40 +++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/heartbeat/docs/getting-started.asciidoc b/heartbeat/docs/getting-started.asciidoc index 21da1fb0547..1cd73dbb04f 100644 --- a/heartbeat/docs/getting-started.asciidoc +++ b/heartbeat/docs/getting-started.asciidoc @@ -96,9 +96,43 @@ was started. Heartbeat adds the `@every` keyword to the syntax provided by the include::{libbeat-dir}/shared/config-check.asciidoc[] +[float] +[[configurelocation]] +=== Step 4: Configure the Heartbeat location + +Heartbeat can be deployed in multiple locations so that you can detect +differences in availability and response times across those locations. +Configure the Heartbeat location to allow {kib} to display location-specific +information on Uptime maps and perform Uptime anomaly detection based +on location. + +To configure the location of a Heartbeat instance, modify the +`add_observer_metadata` processor in +{beatname_lc}.yml+. The following +example specifies the `geo.name` of the `add_observer_metadata` processor as +`us-east-1a`: + +[source,yaml] +---------------------------------------------------------------------- +# ============================ Processors ============================ + +processors: + - add_observer_metadata: + # Optional, but recommended geo settings for the location Heartbeat is running in + geo: <1> + # Token describing this location + name: us-east-1a <2> + # Lat, Lon " + #location: "37.926868, -78.024902" <3> +---------------------------------------------------------------------- +<1> Uncomment the `geo` setting. +<2> Uncomment `name` and assign the name of the location of the Heartbeat server. +<3> Optionally uncomment `location` and assign the latitude and longitude. + +include::{libbeat-dir}/shared/config-check.asciidoc[] + [float] [[setup-assets]] -=== Step 4: Set up assets +=== Step 5: Set up assets {beatname_uc} comes with predefined assets for parsing, indexing, and visualizing your data. To load these assets: @@ -128,7 +162,7 @@ environment. If you're using a different output, such as {ls}, see [float] [[start]] -=== Step 5: Start Heartbeat +=== Step 6: Start Heartbeat Before starting {beatname_uc}, modify the user credentials in +{beatname_lc}.yml+ and specify a user who is @@ -145,7 +179,7 @@ events to your defined output. [float] [[view-data]] -=== Step 6: View your data in {kib} +=== Step 7: View your data in {kib} {beatname_uc} comes with pre-built {kib} dashboards and UIs for visualizing the status of your services. The dashboards are available in the From 00d0419b9d1cadc4b2feb3c0676d7661d4786cfa Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Wed, 7 Oct 2020 15:03:22 +0100 Subject: [PATCH 49/93] [CI] Change notification channel (#21559) --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index df75ad2ccd1..17041987b27 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -24,7 +24,7 @@ pipeline { PIPELINE_LOG_LEVEL = 'INFO' PYTEST_ADDOPTS = "${params.PYTEST_ADDOPTS}" RUNBLD_DISABLE_NOTIFICATIONS = 'true' - SLACK_CHANNEL = "#beats-ci-builds" + SLACK_CHANNEL = "#beats-build" TERRAFORM_VERSION = "0.12.24" XPACK_MODULE_PATTERN = '^x-pack\\/[a-z0-9]+beat\\/module\\/([^\\/]+)\\/.*' } @@ -122,7 +122,7 @@ pipeline { runbld(stashedTestReports: stashedTestReports, project: env.REPO) } cleanup { - notifyBuildResult(prComment: true, slackComment: true) + notifyBuildResult(prComment: true, slackComment: true, slackNotify: (isBranch() || isTag())) } } } From 030077ce9098bbb419fe92843a2ce3ad25860b20 Mon Sep 17 00:00:00 2001 From: kaiyan-sheng Date: Wed, 7 Oct 2020 08:22:34 -0600 Subject: [PATCH 50/93] [Filebeat S3] Change log.file.path to be nested object (#21624) --- x-pack/filebeat/input/s3/collector.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/x-pack/filebeat/input/s3/collector.go b/x-pack/filebeat/input/s3/collector.go index 2976dd52a5b..bf294f94245 100644 --- a/x-pack/filebeat/input/s3/collector.go +++ b/x-pack/filebeat/input/s3/collector.go @@ -556,8 +556,10 @@ func createEvent(log string, offset int, info s3Info, objectHash string, s3Ctx * Fields: common.MapStr{ "message": log, "log": common.MapStr{ - "offset": int64(offset), - "file.path": constructObjectURL(info), + "offset": int64(offset), + "file": common.MapStr{ + "path": constructObjectURL(info), + }, }, "aws": common.MapStr{ "s3": common.MapStr{ From 048a404985dae344e340b82ddd14698f7fa86bed Mon Sep 17 00:00:00 2001 From: kaiyan-sheng Date: Wed, 7 Oct 2020 08:25:54 -0600 Subject: [PATCH 51/93] Add fips_enabled into all aws filesets (#21626) --- x-pack/filebeat/module/aws/cloudtrail/config/s3.yml | 4 ++++ x-pack/filebeat/module/aws/cloudwatch/config/s3.yml | 4 ++++ x-pack/filebeat/module/aws/ec2/config/s3.yml | 4 ++++ x-pack/filebeat/module/aws/elb/config/s3.yml | 4 ++++ x-pack/filebeat/module/aws/s3access/config/s3.yml | 4 ++++ x-pack/filebeat/module/aws/vpcflow/config/input.yml | 4 ++++ 6 files changed, 24 insertions(+) diff --git a/x-pack/filebeat/module/aws/cloudtrail/config/s3.yml b/x-pack/filebeat/module/aws/cloudtrail/config/s3.yml index 7c455f64f26..ac1caacf21c 100644 --- a/x-pack/filebeat/module/aws/cloudtrail/config/s3.yml +++ b/x-pack/filebeat/module/aws/cloudtrail/config/s3.yml @@ -51,6 +51,10 @@ session_token: {{ .session_token }} role_arn: {{ .role_arn }} {{ end }} +{{ if .fips_enabled }} +fips_enabled: {{ .fips_enabled }} +{{ end }} + tags: {{.tags | tojson}} publisher_pipeline.disable_host: {{ inList .tags "forwarded" }} diff --git a/x-pack/filebeat/module/aws/cloudwatch/config/s3.yml b/x-pack/filebeat/module/aws/cloudwatch/config/s3.yml index 5d2d75dc5d8..bdb0ff350f0 100644 --- a/x-pack/filebeat/module/aws/cloudwatch/config/s3.yml +++ b/x-pack/filebeat/module/aws/cloudwatch/config/s3.yml @@ -37,6 +37,10 @@ session_token: {{ .session_token }} role_arn: {{ .role_arn }} {{ end }} +{{ if .fips_enabled }} +fips_enabled: {{ .fips_enabled }} +{{ end }} + tags: {{.tags | tojson}} publisher_pipeline.disable_host: {{ inList .tags "forwarded" }} diff --git a/x-pack/filebeat/module/aws/ec2/config/s3.yml b/x-pack/filebeat/module/aws/ec2/config/s3.yml index 5d2d75dc5d8..bdb0ff350f0 100644 --- a/x-pack/filebeat/module/aws/ec2/config/s3.yml +++ b/x-pack/filebeat/module/aws/ec2/config/s3.yml @@ -37,6 +37,10 @@ session_token: {{ .session_token }} role_arn: {{ .role_arn }} {{ end }} +{{ if .fips_enabled }} +fips_enabled: {{ .fips_enabled }} +{{ end }} + tags: {{.tags | tojson}} publisher_pipeline.disable_host: {{ inList .tags "forwarded" }} diff --git a/x-pack/filebeat/module/aws/elb/config/s3.yml b/x-pack/filebeat/module/aws/elb/config/s3.yml index 5d2d75dc5d8..bdb0ff350f0 100644 --- a/x-pack/filebeat/module/aws/elb/config/s3.yml +++ b/x-pack/filebeat/module/aws/elb/config/s3.yml @@ -37,6 +37,10 @@ session_token: {{ .session_token }} role_arn: {{ .role_arn }} {{ end }} +{{ if .fips_enabled }} +fips_enabled: {{ .fips_enabled }} +{{ end }} + tags: {{.tags | tojson}} publisher_pipeline.disable_host: {{ inList .tags "forwarded" }} diff --git a/x-pack/filebeat/module/aws/s3access/config/s3.yml b/x-pack/filebeat/module/aws/s3access/config/s3.yml index 5d2d75dc5d8..bdb0ff350f0 100644 --- a/x-pack/filebeat/module/aws/s3access/config/s3.yml +++ b/x-pack/filebeat/module/aws/s3access/config/s3.yml @@ -37,6 +37,10 @@ session_token: {{ .session_token }} role_arn: {{ .role_arn }} {{ end }} +{{ if .fips_enabled }} +fips_enabled: {{ .fips_enabled }} +{{ end }} + tags: {{.tags | tojson}} publisher_pipeline.disable_host: {{ inList .tags "forwarded" }} diff --git a/x-pack/filebeat/module/aws/vpcflow/config/input.yml b/x-pack/filebeat/module/aws/vpcflow/config/input.yml index d0c18047ca4..628196b7d3e 100644 --- a/x-pack/filebeat/module/aws/vpcflow/config/input.yml +++ b/x-pack/filebeat/module/aws/vpcflow/config/input.yml @@ -39,6 +39,10 @@ session_token: {{ .session_token }} role_arn: {{ .role_arn }} {{ end }} +{{ if .fips_enabled }} +fips_enabled: {{ .fips_enabled }} +{{ end }} + {{ else if eq .input "file" }} type: log From cf5fafb157c127c0159e55697ac8a2baf9f1e710 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Wed, 7 Oct 2020 17:56:16 +0200 Subject: [PATCH 52/93] Add openstack ssl provider in add_cloud_metadata (#21590) Add a new provider to query metadata from OpenStack deployments using HTTPS. Add the usual SSL settings available in other features that support TLS. --- CHANGELOG.next.asciidoc | 1 + .../add_cloud_metadata/add_cloud_metadata.go | 16 ++++++++-- .../processors/add_cloud_metadata/config.go | 9 ++++-- .../docs/add_cloud_metadata.asciidoc | 4 +++ .../add_cloud_metadata/http_fetcher.go | 11 +++++-- .../provider_openstack_nova.go | 18 +++++++---- .../provider_openstack_nova_test.go | 30 ++++++++++++++++--- .../add_cloud_metadata/providers.go | 25 +++++++++------- 8 files changed, 87 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 6b4a999e0ec..cc8fb52396a 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -453,6 +453,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Cloud Foundry metadata is cached to disk. {pull}20775[20775] - Add option to select the type of index template to load: legacy, component, index. {pull}21212[21212] - Release `add_cloudfoundry_metadata` as GA. {pull}21525[21525] +- Add support for OpenStack SSL metadata APIs in `add_cloud_metadata`. {pull}21590[21590] *Auditbeat* diff --git a/libbeat/processors/add_cloud_metadata/add_cloud_metadata.go b/libbeat/processors/add_cloud_metadata/add_cloud_metadata.go index de1a8063667..5376d563fca 100644 --- a/libbeat/processors/add_cloud_metadata/add_cloud_metadata.go +++ b/libbeat/processors/add_cloud_metadata/add_cloud_metadata.go @@ -26,6 +26,7 @@ import ( "github.com/elastic/beats/v7/libbeat/beat" "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/common/transport/tlscommon" "github.com/elastic/beats/v7/libbeat/logp" "github.com/elastic/beats/v7/libbeat/processors" jsprocessor "github.com/elastic/beats/v7/libbeat/processors/script/javascript/module/processor" @@ -53,6 +54,7 @@ type addCloudMetadata struct { type initData struct { fetchers []metadataFetcher timeout time.Duration + tlsConfig *tlscommon.TLSConfig overwrite bool } @@ -63,14 +65,24 @@ func New(c *common.Config) (processors.Processor, error) { return nil, errors.Wrap(err, "failed to unpack add_cloud_metadata config") } + tlsConfig, err := tlscommon.LoadTLSConfig(config.TLS) + if err != nil { + return nil, errors.Wrap(err, "TLS configuration load") + } + initProviders := selectProviders(config.Providers, cloudMetaProviders) fetchers, err := setupFetchers(initProviders, c) if err != nil { return nil, err } p := &addCloudMetadata{ - initData: &initData{fetchers, config.Timeout, config.Overwrite}, - logger: logp.NewLogger("add_cloud_metadata"), + initData: &initData{ + fetchers: fetchers, + timeout: config.Timeout, + tlsConfig: tlsConfig, + overwrite: config.Overwrite, + }, + logger: logp.NewLogger("add_cloud_metadata"), } go p.init() diff --git a/libbeat/processors/add_cloud_metadata/config.go b/libbeat/processors/add_cloud_metadata/config.go index 08f4a241483..93a31137592 100644 --- a/libbeat/processors/add_cloud_metadata/config.go +++ b/libbeat/processors/add_cloud_metadata/config.go @@ -20,12 +20,15 @@ package add_cloud_metadata import ( "fmt" "time" + + "github.com/elastic/beats/v7/libbeat/common/transport/tlscommon" ) type config struct { - Timeout time.Duration `config:"timeout"` // Amount of time to wait for responses from the metadata services. - Overwrite bool `config:"overwrite"` // Overwrite if cloud.* fields already exist. - Providers providerList `config:"providers"` // List of providers to probe + Timeout time.Duration `config:"timeout"` // Amount of time to wait for responses from the metadata services. + TLS *tlscommon.Config `config:"ssl"` // TLS configuration + Overwrite bool `config:"overwrite"` // Overwrite if cloud.* fields already exist. + Providers providerList `config:"providers"` // List of providers to probe } type providerList []string diff --git a/libbeat/processors/add_cloud_metadata/docs/add_cloud_metadata.asciidoc b/libbeat/processors/add_cloud_metadata/docs/add_cloud_metadata.asciidoc index 41c0dd6d9f3..44497f31539 100644 --- a/libbeat/processors/add_cloud_metadata/docs/add_cloud_metadata.asciidoc +++ b/libbeat/processors/add_cloud_metadata/docs/add_cloud_metadata.asciidoc @@ -52,12 +52,16 @@ List of names the `providers` setting supports: - "aws", or "ec2" for Amazon Web Services (enabled by default). - "gcp" for Google Copmute Enging (enabled by default). - "openstack", or "nova" for Openstack Nova (enabled by default). +- "openstack-ssl", or "nova-ssl" for Openstack Nova when SSL metadata APIs are enabled (enabled by default). - "tencent", or "qcloud" for Tencent Cloud (disabled by default). The third optional configuration setting is `overwrite`. When `overwrite` is `true`, `add_cloud_metadata` overwrites existing `cloud.*` fields (`false` by default). +The `add_cloud_metadata` processor supports SSL options to configure the http +client used to query cloud metadata. See <> for more information. + The metadata that is added to events varies by hosting provider. Below are examples for each of the supported providers. diff --git a/libbeat/processors/add_cloud_metadata/http_fetcher.go b/libbeat/processors/add_cloud_metadata/http_fetcher.go index e9b18478661..e337edd5be9 100644 --- a/libbeat/processors/add_cloud_metadata/http_fetcher.go +++ b/libbeat/processors/add_cloud_metadata/http_fetcher.go @@ -27,6 +27,7 @@ import ( "github.com/pkg/errors" "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/common/transport/tlscommon" ) type httpMetadataFetcher struct { @@ -127,9 +128,15 @@ func (f *httpMetadataFetcher) fetchRaw( // getMetadataURLs loads config and generates the metadata URLs. func getMetadataURLs(c *common.Config, defaultHost string, metadataURIs []string) ([]string, error) { + return getMetadataURLsWithScheme(c, "http", defaultHost, metadataURIs) +} + +// getMetadataURLsWithScheme loads config and generates the metadata URLs. +func getMetadataURLsWithScheme(c *common.Config, scheme string, defaultHost string, metadataURIs []string) ([]string, error) { var urls []string config := struct { - MetadataHostAndPort string `config:"host"` // Specifies the host and port of the metadata service (for testing purposes only). + MetadataHostAndPort string `config:"host"` // Specifies the host and port of the metadata service (for testing purposes only). + TLSConfig *tlscommon.Config `config:"ssl"` }{ MetadataHostAndPort: defaultHost, } @@ -138,7 +145,7 @@ func getMetadataURLs(c *common.Config, defaultHost string, metadataURIs []string return urls, errors.Wrap(err, "failed to unpack add_cloud_metadata config") } for _, uri := range metadataURIs { - urls = append(urls, "http://"+config.MetadataHostAndPort+uri) + urls = append(urls, scheme+"://"+config.MetadataHostAndPort+uri) } return urls, nil } diff --git a/libbeat/processors/add_cloud_metadata/provider_openstack_nova.go b/libbeat/processors/add_cloud_metadata/provider_openstack_nova.go index 17bc5abf689..7c9a997e0e2 100644 --- a/libbeat/processors/add_cloud_metadata/provider_openstack_nova.go +++ b/libbeat/processors/add_cloud_metadata/provider_openstack_nova.go @@ -32,16 +32,24 @@ const ( // OpenStack Nova Metadata Service // Document https://docs.openstack.org/nova/latest/user/metadata-service.html var openstackNovaMetadataFetcher = provider{ - Name: "openstack-nova", + Name: "openstack-nova", + Local: true, + Create: buildOpenstackNovaCreate("http"), +} - Local: true, +var openstackNovaSSLMetadataFetcher = provider{ + Name: "openstack-nova-ssl", + Local: true, + Create: buildOpenstackNovaCreate("https"), +} - Create: func(provider string, c *common.Config) (metadataFetcher, error) { +func buildOpenstackNovaCreate(scheme string) func(provider string, c *common.Config) (metadataFetcher, error) { + return func(provider string, c *common.Config) (metadataFetcher, error) { osSchema := func(m map[string]interface{}) common.MapStr { return common.MapStr(m) } - urls, err := getMetadataURLs(c, metadataHost, []string{ + urls, err := getMetadataURLsWithScheme(c, scheme, metadataHost, []string{ osMetadataInstanceIDURI, osMetadataInstanceTypeURI, osMetadataHostnameURI, @@ -71,5 +79,5 @@ var openstackNovaMetadataFetcher = provider{ } fetcher := &httpMetadataFetcher{"openstack", nil, responseHandlers, osSchema} return fetcher, nil - }, + } } diff --git a/libbeat/processors/add_cloud_metadata/provider_openstack_nova_test.go b/libbeat/processors/add_cloud_metadata/provider_openstack_nova_test.go index d5c38a8437b..0a63c026cde 100644 --- a/libbeat/processors/add_cloud_metadata/provider_openstack_nova_test.go +++ b/libbeat/processors/add_cloud_metadata/provider_openstack_nova_test.go @@ -29,8 +29,8 @@ import ( "github.com/elastic/beats/v7/libbeat/logp" ) -func initOpenstackNovaTestServer() *httptest.Server { - return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { +func openstackNovaMetadataHandler() http.HandlerFunc { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.RequestURI == osMetadataInstanceIDURI { w.Write([]byte("i-0000ffac")) return @@ -49,13 +49,13 @@ func initOpenstackNovaTestServer() *httptest.Server { } http.Error(w, "not found", http.StatusNotFound) - })) + }) } func TestRetrieveOpenstackNovaMetadata(t *testing.T) { logp.TestingSetup() - server := initOpenstackNovaTestServer() + server := httptest.NewServer(openstackNovaMetadataHandler()) defer server.Close() config, err := common.NewConfigFrom(map[string]interface{}{ @@ -66,6 +66,28 @@ func TestRetrieveOpenstackNovaMetadata(t *testing.T) { t.Fatal(err) } + assertOpenstackNova(t, config) +} + +func TestRetrieveOpenstackNovaMetadataWithHTTPS(t *testing.T) { + logp.TestingSetup() + + server := httptest.NewTLSServer(openstackNovaMetadataHandler()) + defer server.Close() + + config, err := common.NewConfigFrom(map[string]interface{}{ + "host": server.Listener.Addr().String(), + "ssl.verification_mode": "none", + }) + + if err != nil { + t.Fatal(err) + } + + assertOpenstackNova(t, config) +} + +func assertOpenstackNova(t *testing.T, config *common.Config) { p, err := New(config) if err != nil { t.Fatal(err) diff --git a/libbeat/processors/add_cloud_metadata/providers.go b/libbeat/processors/add_cloud_metadata/providers.go index f93e8cf78f4..ea03b64258a 100644 --- a/libbeat/processors/add_cloud_metadata/providers.go +++ b/libbeat/processors/add_cloud_metadata/providers.go @@ -51,17 +51,19 @@ type result struct { } var cloudMetaProviders = map[string]provider{ - "alibaba": alibabaCloudMetadataFetcher, - "ecs": alibabaCloudMetadataFetcher, - "azure": azureVMMetadataFetcher, - "digitalocean": doMetadataFetcher, - "aws": ec2MetadataFetcher, - "ec2": ec2MetadataFetcher, - "gcp": gceMetadataFetcher, - "openstack": openstackNovaMetadataFetcher, - "nova": openstackNovaMetadataFetcher, - "qcloud": qcloudMetadataFetcher, - "tencent": qcloudMetadataFetcher, + "alibaba": alibabaCloudMetadataFetcher, + "ecs": alibabaCloudMetadataFetcher, + "azure": azureVMMetadataFetcher, + "digitalocean": doMetadataFetcher, + "aws": ec2MetadataFetcher, + "ec2": ec2MetadataFetcher, + "gcp": gceMetadataFetcher, + "openstack": openstackNovaMetadataFetcher, + "nova": openstackNovaMetadataFetcher, + "openstack-ssl": openstackNovaSSLMetadataFetcher, + "nova-ssl": openstackNovaSSLMetadataFetcher, + "qcloud": qcloudMetadataFetcher, + "tencent": qcloudMetadataFetcher, } func selectProviders(configList providerList, providers map[string]provider) map[string]provider { @@ -138,6 +140,7 @@ func (p *addCloudMetadata) fetchMetadata() *result { Timeout: p.initData.timeout, KeepAlive: 0, }).DialContext, + TLSClientConfig: p.initData.tlsConfig.ToConfig(), }, } From 7028e2c9779bff69f7d526af69eb804c024e5776 Mon Sep 17 00:00:00 2001 From: Marc Guasch Date: Wed, 7 Oct 2020 18:47:33 +0200 Subject: [PATCH 53/93] Fix cyberark/corepas pipeline (#21643) --- .../cyberark/corepas/ingest/pipeline.yml | 2 +- .../corepas/test/generated.log-expected.json | 394 +++++++++--------- 2 files changed, 198 insertions(+), 198 deletions(-) diff --git a/x-pack/filebeat/module/cyberark/corepas/ingest/pipeline.yml b/x-pack/filebeat/module/cyberark/corepas/ingest/pipeline.yml index ffe90e79f85..4e401931415 100644 --- a/x-pack/filebeat/module/cyberark/corepas/ingest/pipeline.yml +++ b/x-pack/filebeat/module/cyberark/corepas/ingest/pipeline.yml @@ -55,7 +55,7 @@ processors: ignore_missing: true - append: field: related.hosts - value: '{{host.hostname server.domain}}' + value: '{{host.hostname}}' allow_duplicates: false if: ctx?.host?.hostname != null && ctx.host?.hostname != '' - append: diff --git a/x-pack/filebeat/module/cyberark/corepas/test/generated.log-expected.json b/x-pack/filebeat/module/cyberark/corepas/test/generated.log-expected.json index 4056ed473ca..90805d72bcd 100644 --- a/x-pack/filebeat/module/cyberark/corepas/test/generated.log-expected.json +++ b/x-pack/filebeat/module/cyberark/corepas/test/generated.log-expected.json @@ -20,9 +20,9 @@ "10.208.15.216" ], "related.user": [ + "quasiarc", "itv", - "utl", - "quasiarc" + "utl" ], "rsa.db.index": "nes", "rsa.internal.event_desc": "pexe", @@ -67,13 +67,13 @@ "iatnu3810.mail.localdomain" ], "related.ip": [ - "10.92.136.230", - "10.175.75.18" + "10.175.75.18", + "10.92.136.230" ], "related.user": [ - "dolore", + "orev", "nnumqu", - "orev" + "dolore" ], "rsa.db.database": "umdo", "rsa.db.index": "vol", @@ -130,13 +130,13 @@ "anti4454.api.example" ], "related.ip": [ - "10.46.185.46", - "10.51.132.10" + "10.51.132.10", + "10.46.185.46" ], "related.user": [ - "incid", + "serror", "nse", - "serror" + "incid" ], "rsa.db.database": "byC", "rsa.db.index": "tur", @@ -197,9 +197,9 @@ "10.53.192.140" ], "related.user": [ + "psumquia", "atcup", - "ptass", - "psumquia" + "ptass" ], "rsa.db.database": "aperi", "rsa.db.index": "llumd", @@ -253,9 +253,9 @@ "10.81.199.122" ], "related.user": [ + "oremips", "eos", - "giatq", - "oremips" + "giatq" ], "rsa.db.index": "tempo", "rsa.internal.event_desc": "uian", @@ -304,9 +304,9 @@ "10.139.186.201" ], "related.user": [ - "uam", "tcupida", - "aboris" + "aboris", + "uam" ], "rsa.db.database": "isiu", "rsa.db.index": "iatisu", @@ -367,9 +367,9 @@ "10.104.111.129" ], "related.user": [ + "ele", "etconsec", - "ipis", - "ele" + "ipis" ], "rsa.db.database": "riat", "rsa.db.index": "umdolor", @@ -423,9 +423,9 @@ "10.116.120.216" ], "related.user": [ - "quiratio", + "animi", "umdo", - "animi" + "quiratio" ], "rsa.db.index": "oll", "rsa.internal.event_desc": "rumet", @@ -470,13 +470,13 @@ "isqu7224.localdomain" ], "related.ip": [ - "10.57.40.29", - "10.62.54.220" + "10.62.54.220", + "10.57.40.29" ], "related.user": [ "psum", - "rnatura", - "taevi" + "taevi", + "rnatura" ], "rsa.db.database": "emeumfug", "rsa.db.index": "omn", @@ -530,9 +530,9 @@ "10.74.237.180" ], "related.user": [ - "ema", + "tnon", "cup", - "tnon" + "ema" ], "rsa.db.index": "remeumf", "rsa.internal.event_desc": "lup", @@ -618,8 +618,8 @@ "10.74.253.127" ], "related.user": [ - "onproide", "icab", + "onproide", "tema" ], "rsa.db.index": "mqui", @@ -664,8 +664,8 @@ "tlabo6088.www.localdomain" ], "related.ip": [ - "10.189.109.245", - "10.92.8.15" + "10.92.8.15", + "10.189.109.245" ], "related.user": [ "inima", @@ -766,8 +766,8 @@ "10.18.109.121" ], "related.user": [ - "hil", "tatn", + "hil", "pida" ], "rsa.db.index": "quip", @@ -817,9 +817,9 @@ "10.63.37.192" ], "related.user": [ + "reetd", "iunt", - "equep", - "reetd" + "equep" ], "rsa.db.database": "aliqu", "rsa.db.index": "mipsumd", @@ -880,9 +880,9 @@ "10.47.202.102" ], "related.user": [ - "run", + "ice", "ntor", - "ice" + "run" ], "rsa.db.database": "ite", "rsa.db.index": "iquipex", @@ -942,8 +942,8 @@ "10.106.239.55" ], "related.user": [ - "itquiin", - "serunt" + "serunt", + "itquiin" ], "rsa.db.database": "itame", "rsa.db.index": "oluptas", @@ -999,13 +999,13 @@ "etMalor4236.www5.host" ], "related.ip": [ - "10.125.160.129", - "10.53.168.235" + "10.53.168.235", + "10.125.160.129" ], "related.user": [ + "one", "abi", - "ione", - "one" + "ione" ], "rsa.db.database": "sperna", "rsa.db.index": "estia", @@ -1066,8 +1066,8 @@ "10.227.177.121" ], "related.user": [ - "liqui", "tasuntex", + "liqui", "iduntu" ], "rsa.db.database": "rvel", @@ -1125,16 +1125,16 @@ "process.name": "laboree.exe", "process.pid": 6501, "related.hosts": [ - "", + "xeacomm6855.api.corp", "nsecte3304.mail.corp" ], "related.ip": [ - "10.167.85.181", - "10.98.182.220" + "10.98.182.220", + "10.167.85.181" ], "related.user": [ - "fde", - "econs" + "econs", + "fde" ], "rsa.db.database": "equat", "rsa.internal.event_desc": "orpor", @@ -1189,9 +1189,9 @@ "10.89.208.95" ], "related.user": [ - "iciadese", "icabo", - "sintoc" + "sintoc", + "iciadese" ], "rsa.db.index": "eni", "rsa.internal.event_desc": "rcitati", @@ -1240,9 +1240,9 @@ "10.72.148.32" ], "related.user": [ - "uteirure", "tDuisaut", - "luptatev" + "luptatev", + "uteirure" ], "rsa.db.database": "uamest", "rsa.db.index": "uae", @@ -1362,12 +1362,12 @@ "tnonpro7635.localdomain" ], "related.ip": [ - "10.192.34.76", - "10.213.144.249" + "10.213.144.249", + "10.192.34.76" ], "related.user": [ - "lore", "temqu", + "lore", "iquipe" ], "rsa.db.database": "gnamal", @@ -1482,9 +1482,9 @@ "10.143.193.199" ], "related.user": [ - "tqu", + "niamqui", "quid", - "niamqui" + "tqu" ], "rsa.db.index": "inci", "rsa.internal.event_desc": "eroinBCS", @@ -1533,9 +1533,9 @@ "10.65.175.9" ], "related.user": [ + "umqu", "ritatise", - "essequam", - "umqu" + "essequam" ], "rsa.db.database": "ender", "rsa.db.index": "entorev", @@ -1589,9 +1589,9 @@ "10.205.72.243" ], "related.user": [ - "isiuta", "tatn", - "umdolo" + "umdolo", + "isiuta" ], "rsa.db.index": "proide", "rsa.internal.event_desc": "ameiusm", @@ -1633,9 +1633,9 @@ "10.107.9.163" ], "related.user": [ - "sit", + "mac", "mquisno", - "mac" + "sit" ], "rsa.db.index": "sit", "rsa.internal.event_desc": "tdol", @@ -1677,9 +1677,9 @@ "10.80.101.72" ], "related.user": [ + "asiarc", "umSe", - "quidexea", - "asiarc" + "quidexea" ], "rsa.db.index": "veli", "rsa.internal.event_desc": "quatu", @@ -1728,9 +1728,9 @@ "10.39.10.155" ], "related.user": [ - "urExcept", + "ptass", "aboreetd", - "ptass" + "urExcept" ], "rsa.db.database": "teirured", "rsa.db.index": "dolorem", @@ -1828,9 +1828,9 @@ "10.71.238.250" ], "related.user": [ + "reseo", "moenimi", - "aec", - "reseo" + "aec" ], "rsa.db.index": "mac", "rsa.internal.event_desc": "quamest", @@ -1875,13 +1875,13 @@ "rum5798.home" ], "related.ip": [ - "10.226.20.199", - "10.226.101.180" + "10.226.101.180", + "10.226.20.199" ], "related.user": [ - "ritt", "rationev", - "veniamqu" + "veniamqu", + "ritt" ], "rsa.db.database": "conse", "rsa.db.index": "imveniam", @@ -1943,9 +1943,9 @@ "10.134.65.15" ], "related.user": [ - "quaUten", + "cab", "utaliqu", - "cab" + "quaUten" ], "rsa.db.database": "isciv", "rsa.db.index": "nofd", @@ -2002,8 +2002,8 @@ "10.70.147.120" ], "related.user": [ - "cidunt", "tten", + "cidunt", "emqu" ], "rsa.db.index": "eaqu", @@ -2053,9 +2053,9 @@ "10.178.242.100" ], "related.user": [ + "idid", "dqu", - "loi", - "idid" + "loi" ], "rsa.db.database": "tenatuse", "rsa.db.index": "ullamcor", @@ -2109,9 +2109,9 @@ "10.211.179.168" ], "related.user": [ - "ritati", "untincul", - "mmodoc" + "mmodoc", + "ritati" ], "rsa.db.index": "emvele", "rsa.internal.event_desc": "oluptas", @@ -2153,9 +2153,9 @@ "10.30.243.163" ], "related.user": [ - "illu", "mven", - "dolore" + "dolore", + "illu" ], "rsa.db.index": "idol", "rsa.internal.event_desc": "lore", @@ -2200,12 +2200,12 @@ "dictasun3878.internal.localhost" ], "related.ip": [ - "10.212.214.4", - "10.6.79.159" + "10.6.79.159", + "10.212.214.4" ], "related.user": [ - "amvo", "quid", + "amvo", "midestl" ], "rsa.db.database": "urExce", @@ -2267,8 +2267,8 @@ "10.237.170.202" ], "related.user": [ - "liquide", "rcit", + "liquide", "atDu" ], "rsa.db.database": "taedict", @@ -2330,8 +2330,8 @@ "10.228.118.81" ], "related.user": [ - "emoe", "tatemU", + "emoe", "itasper" ], "rsa.db.database": "toditaut", @@ -2389,13 +2389,13 @@ "esseq7889.www.invalid" ], "related.ip": [ - "10.49.71.118", - "10.234.165.130" + "10.234.165.130", + "10.49.71.118" ], "related.user": [ "henderit", - "emip", - "iuntNequ" + "iuntNequ", + "emip" ], "rsa.db.database": "veniamqu", "rsa.db.index": "atquo", @@ -2449,9 +2449,9 @@ "10.199.5.49" ], "related.user": [ - "emip", "turadipi", - "olorema" + "olorema", + "emip" ], "rsa.db.index": "ataevi", "rsa.internal.event_desc": "minim", @@ -2493,9 +2493,9 @@ "10.193.219.34" ], "related.user": [ - "uamei", + "olorem", "utlabo", - "olorem" + "uamei" ], "rsa.db.index": "nse", "rsa.internal.event_desc": "orisni", @@ -2540,12 +2540,12 @@ "tem6815.home" ], "related.ip": [ - "10.174.185.109", - "10.120.167.217" + "10.120.167.217", + "10.174.185.109" ], "related.user": [ - "dolorem", "rsp", + "dolorem", "animid" ], "rsa.db.database": "tsuntinc", @@ -2603,12 +2603,12 @@ "mporainc2064.home" ], "related.ip": [ - "10.141.213.219", - "10.117.137.159" + "10.117.137.159", + "10.141.213.219" ], "related.user": [ - "accusa", "atev", + "accusa", "ate" ], "rsa.db.database": "nibus", @@ -2666,13 +2666,13 @@ "caboNem1043.internal.home" ], "related.ip": [ - "10.166.90.130", - "10.94.224.229" + "10.94.224.229", + "10.166.90.130" ], "related.user": [ "eavol", - "etconsec", - "rem" + "rem", + "etconsec" ], "rsa.db.database": "oditempo", "rsa.db.index": "deF", @@ -2731,13 +2731,13 @@ "tatio6513.www.invalid" ], "related.ip": [ - "10.201.81.46", - "10.38.28.151" + "10.38.28.151", + "10.201.81.46" ], "related.user": [ + "mipsumqu", "incidid", - "tiumto", - "mipsumqu" + "tiumto" ], "rsa.db.database": "abor", "rsa.db.index": "adol", @@ -2796,12 +2796,12 @@ "dolori6232.api.invalid" ], "related.ip": [ - "10.214.245.95", - "10.255.28.56" + "10.255.28.56", + "10.214.245.95" ], "related.user": [ - "umdolors", "rerepre", + "umdolors", "uptatem" ], "rsa.db.database": "odt", @@ -2900,8 +2900,8 @@ "10.141.200.133" ], "related.user": [ - "iame", "ess", + "iame", "enim" ], "rsa.db.index": "nofdeFi", @@ -2945,8 +2945,8 @@ ], "related.user": [ "runtmo", - "illoi", - "ugi" + "ugi", + "illoi" ], "rsa.db.index": "eetdo", "rsa.internal.event_desc": "quaer", @@ -2991,13 +2991,13 @@ "mestq2106.api.host" ], "related.ip": [ - "10.41.89.217", - "10.39.143.155" + "10.39.143.155", + "10.41.89.217" ], "related.user": [ - "sedquiac", "tem", - "tperspic" + "tperspic", + "sedquiac" ], "rsa.db.database": "radipis", "rsa.db.index": "nse", @@ -3058,8 +3058,8 @@ "10.5.5.1" ], "related.user": [ - "minim", "CSe", + "minim", "unt" ], "rsa.db.database": "atu", @@ -3121,9 +3121,9 @@ "10.168.132.175" ], "related.user": [ - "eursinto", + "iamea", "giatquov", - "iamea" + "eursinto" ], "rsa.db.database": "ici", "rsa.db.index": "iquaUt", @@ -3177,9 +3177,9 @@ "10.123.154.17" ], "related.user": [ + "quiac", "dolorsi", - "lmo", - "quiac" + "lmo" ], "rsa.db.index": "idunt", "rsa.internal.event_desc": "usantiu", @@ -3270,9 +3270,9 @@ "10.126.205.76" ], "related.user": [ - "rsitvol", "Nemoenim", - "iati" + "iati", + "rsitvol" ], "rsa.db.index": "eFini", "rsa.internal.event_desc": "acom", @@ -3317,13 +3317,13 @@ "fic5107.home" ], "related.ip": [ - "10.169.101.161", - "10.164.66.154" + "10.164.66.154", + "10.169.101.161" ], "related.user": [ - "orissu", + "eufug", "ine", - "eufug" + "orissu" ], "rsa.db.database": "stquidol", "rsa.db.index": "imadmini", @@ -3377,8 +3377,8 @@ "10.70.83.200" ], "related.user": [ - "metco", "riat", + "metco", "ihilmole" ], "rsa.db.index": "urQuis", @@ -3424,13 +3424,13 @@ "onpr47.api.home" ], "related.ip": [ - "10.207.97.192", - "10.134.55.11" + "10.134.55.11", + "10.207.97.192" ], "related.user": [ + "madminim", "tanimid", - "mmod", - "madminim" + "mmod" ], "rsa.db.database": "tetura", "rsa.db.index": "uptasnul", @@ -3492,8 +3492,8 @@ ], "related.user": [ "eritq", - "oinBCSed", - "texplica" + "texplica", + "oinBCSed" ], "rsa.db.database": "lit", "rsa.db.index": "ritati", @@ -3550,8 +3550,8 @@ "eufugia4481.corp" ], "related.ip": [ - "10.41.232.147", - "10.61.175.217" + "10.61.175.217", + "10.41.232.147" ], "related.user": [ "runtm", @@ -3610,9 +3610,9 @@ "10.150.30.95" ], "related.user": [ - "mini", + "atnonpr", "uisnos", - "atnonpr" + "mini" ], "rsa.db.index": "smod", "rsa.internal.event_desc": "isn", @@ -3654,9 +3654,9 @@ "10.98.71.45" ], "related.user": [ - "fugitse", "CSe", - "onse" + "onse", + "fugitse" ], "rsa.db.index": "Dui", "rsa.internal.event_desc": "isci", @@ -3742,8 +3742,8 @@ "10.197.203.167" ], "related.user": [ - "iumdo", "uta", + "iumdo", "eserun" ], "rsa.db.index": "smo", @@ -3786,9 +3786,9 @@ "10.187.170.23" ], "related.user": [ - "ibusBo", + "enima", "sectetu", - "enima" + "ibusBo" ], "rsa.db.index": "uido", "rsa.internal.event_desc": "lab", @@ -3837,9 +3837,9 @@ "10.250.248.215" ], "related.user": [ - "aevitaed", "tinculpa", - "quaeratv" + "quaeratv", + "aevitaed" ], "rsa.db.database": "lica", "rsa.db.index": "uisnos", @@ -3895,8 +3895,8 @@ "osa3211.www5.example" ], "related.ip": [ - "10.147.154.118", - "10.146.57.23" + "10.146.57.23", + "10.147.154.118" ], "related.user": [ "tateveli", @@ -3997,9 +3997,9 @@ "10.154.172.82" ], "related.user": [ - "onnumqua", + "tetura", "nesci", - "tetura" + "onnumqua" ], "rsa.db.index": "oinBCSed", "rsa.internal.event_desc": "ntor", @@ -4042,8 +4042,8 @@ ], "related.user": [ "tpers", - "midestl", - "expl" + "expl", + "midestl" ], "rsa.db.index": "olu", "rsa.internal.event_desc": "odocons", @@ -4085,8 +4085,8 @@ "10.178.160.245" ], "related.user": [ - "fdeFinib", "turQuis", + "fdeFinib", "olupta" ], "rsa.db.index": "rsint", @@ -4195,13 +4195,13 @@ "nimve2787.mail.test" ], "related.ip": [ - "10.65.207.234", - "10.222.32.183" + "10.222.32.183", + "10.65.207.234" ], "related.user": [ "eve", - "itame", - "eruntmo" + "eruntmo", + "itame" ], "rsa.db.database": "udexerc", "rsa.db.index": "volup", @@ -4255,9 +4255,9 @@ "10.16.181.60" ], "related.user": [ + "olore", "gnama", - "oinven", - "olore" + "oinven" ], "rsa.db.index": "uatu", "rsa.internal.event_desc": "nderiti", @@ -4343,9 +4343,9 @@ "10.204.214.98" ], "related.user": [ + "eprehe", "porissus", - "tdolo", - "eprehe" + "tdolo" ], "rsa.db.index": "abo", "rsa.internal.event_desc": "ecte", @@ -4387,8 +4387,8 @@ "10.223.178.192" ], "related.user": [ - "etc", "evel", + "etc", "moenimip" ], "rsa.db.index": "iarchit", @@ -4434,13 +4434,13 @@ "ama6820.mail.example" ], "related.ip": [ - "10.26.137.126", - "10.26.33.181" + "10.26.33.181", + "10.26.137.126" ], "related.user": [ - "taevit", "ati", - "audant" + "audant", + "taevit" ], "rsa.db.database": "com", "rsa.db.index": "mveni", @@ -4560,13 +4560,13 @@ "lit4112.www.localhost" ], "related.ip": [ - "10.107.24.54", - "10.10.174.253" + "10.10.174.253", + "10.107.24.54" ], "related.user": [ "itinvo", - "uptasn", - "hend" + "hend", + "uptasn" ], "rsa.db.database": "lup", "rsa.db.index": "isau", @@ -4621,9 +4621,9 @@ "10.87.92.17" ], "related.user": [ + "eeufug", "luptate", - "tamr", - "eeufug" + "tamr" ], "rsa.db.index": "oreeufug", "rsa.internal.event_desc": "ura", @@ -4676,9 +4676,9 @@ "10.161.51.135" ], "related.user": [ - "Finibus", "accus", - "asper" + "asper", + "Finibus" ], "rsa.db.database": "litani", "rsa.db.index": "arch", @@ -4732,9 +4732,9 @@ "10.51.17.32" ], "related.user": [ - "itten", "mquido", - "llum" + "llum", + "itten" ], "rsa.db.index": "uscipit", "rsa.internal.event_desc": "llitani", @@ -4777,8 +4777,8 @@ ], "related.user": [ "ollita", - "mmodicon", - "cusa" + "cusa", + "mmodicon" ], "rsa.db.index": "ercitati", "rsa.internal.event_desc": "pteurs", @@ -4824,13 +4824,13 @@ "uidol6868.mail.localdomain" ], "related.ip": [ - "10.114.0.148", - "10.198.187.144" + "10.198.187.144", + "10.114.0.148" ], "related.user": [ "equatD", - "rsitamet", - "ons" + "ons", + "rsitamet" ], "rsa.db.database": "periam", "rsa.db.index": "umiurer", @@ -4888,9 +4888,9 @@ "10.61.140.120" ], "related.user": [ - "loru", + "naaliq", "equa", - "naaliq" + "loru" ], "rsa.db.index": "umfugiat", "rsa.internal.event_desc": "ora", @@ -4935,13 +4935,13 @@ "ptat4878.lan" ], "related.ip": [ - "10.149.238.108", - "10.93.24.151" + "10.93.24.151", + "10.149.238.108" ], "related.user": [ "ite", - "nven", - "sequamn" + "sequamn", + "nven" ], "rsa.db.database": "fugi", "rsa.db.index": "nesciu", @@ -4995,9 +4995,9 @@ "10.101.45.225" ], "related.user": [ - "emi", "uinesc", - "cipitla" + "cipitla", + "emi" ], "rsa.db.index": "caecat", "rsa.internal.event_desc": "tsunt", @@ -5040,9 +5040,9 @@ "10.2.204.161" ], "related.user": [ - "quela", + "eumfugia", "ore", - "eumfugia" + "quela" ], "rsa.db.index": "olup", "rsa.internal.event_desc": "quuntur", @@ -5139,8 +5139,8 @@ "10.151.110.250" ], "related.user": [ - "neavol", "tla", + "neavol", "pidatatn" ], "rsa.db.database": "itaedict", @@ -5258,9 +5258,9 @@ "10.128.102.130" ], "related.user": [ - "que", + "sequatu", "ore", - "sequatu" + "que" ], "rsa.db.index": "exerci", "rsa.internal.event_desc": "olu", @@ -5309,8 +5309,8 @@ "10.200.162.248" ], "related.user": [ - "onnu", "reseo", + "onnu", "doloremi" ], "rsa.db.database": "billo", @@ -5365,8 +5365,8 @@ "10.103.215.159" ], "related.user": [ - "volup", "apa", + "volup", "atatn" ], "rsa.db.index": "atcupi", From 189171723ce69e3a235ef16a80c870db74fba682 Mon Sep 17 00:00:00 2001 From: Blake Rouse Date: Wed, 7 Oct 2020 18:52:51 -0500 Subject: [PATCH 54/93] [Elastic Agent] Reload fleet.kibana.hosts from policy change (#21599) * Update the connected client for kibana from policy change. * Fix vet. * Add changelog. * Add protocol compare. * Rollback protocol and hosts on failure. --- x-pack/elastic-agent/CHANGELOG.next.asciidoc | 1 + .../pkg/agent/application/fleet_acker.go | 4 + .../pkg/agent/application/fleet_gateway.go | 4 + .../handler_action_policy_change.go | 107 +++++++++++++++++- .../handler_action_policy_change_test.go | 45 +++++++- .../pkg/agent/application/managed_mode.go | 16 ++- .../agent/application/managed_mode_test.go | 12 +- .../pkg/agent/storage/storage.go | 8 +- 8 files changed, 182 insertions(+), 15 deletions(-) diff --git a/x-pack/elastic-agent/CHANGELOG.next.asciidoc b/x-pack/elastic-agent/CHANGELOG.next.asciidoc index a0cd6f6291e..73dfe8ff9a4 100644 --- a/x-pack/elastic-agent/CHANGELOG.next.asciidoc +++ b/x-pack/elastic-agent/CHANGELOG.next.asciidoc @@ -33,3 +33,4 @@ - Send updating state {pull}21461[21461] - Add `elastic.agent.id` and `elastic.agent.version` to published events from filebeat and metricbeat {pull}21543[21543] - Add `upgrade` subcommand to perform upgrade of installed Elastic Agent {pull}21425[21425] +- Update `fleet.yml` and Kibana hosts when a policy change updates the Kibana hosts {pull}21599[21599] diff --git a/x-pack/elastic-agent/pkg/agent/application/fleet_acker.go b/x-pack/elastic-agent/pkg/agent/application/fleet_acker.go index ba324f2d7de..4544fa8a772 100644 --- a/x-pack/elastic-agent/pkg/agent/application/fleet_acker.go +++ b/x-pack/elastic-agent/pkg/agent/application/fleet_acker.go @@ -39,6 +39,10 @@ func newActionAcker( }, nil } +func (f *actionAcker) SetClient(client clienter) { + f.client = client +} + func (f *actionAcker) Ack(ctx context.Context, action fleetapi.Action) error { // checkin agentID := f.agentInfo.AgentID() diff --git a/x-pack/elastic-agent/pkg/agent/application/fleet_gateway.go b/x-pack/elastic-agent/pkg/agent/application/fleet_gateway.go index 4bb9d2e6280..21e7bfd4589 100644 --- a/x-pack/elastic-agent/pkg/agent/application/fleet_gateway.go +++ b/x-pack/elastic-agent/pkg/agent/application/fleet_gateway.go @@ -253,3 +253,7 @@ func (f *fleetGateway) stop() { close(f.done) f.wg.Wait() } + +func (f *fleetGateway) SetClient(client clienter) { + f.client = client +} diff --git a/x-pack/elastic-agent/pkg/agent/application/handler_action_policy_change.go b/x-pack/elastic-agent/pkg/agent/application/handler_action_policy_change.go index 81dc1444816..8821700ee74 100644 --- a/x-pack/elastic-agent/pkg/agent/application/handler_action_policy_change.go +++ b/x-pack/elastic-agent/pkg/agent/application/handler_action_policy_change.go @@ -5,17 +5,36 @@ package application import ( + "bytes" "context" "fmt" + "io" + "sort" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/info" + + "gopkg.in/yaml.v2" + + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/configuration" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/storage" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/config" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/fleetapi" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/kibana" ) +type clientSetter interface { + SetClient(clienter) +} + type handlerPolicyChange struct { - log *logger.Logger - emitter emitterFunc + log *logger.Logger + emitter emitterFunc + agentInfo *info.AgentInfo + config *configuration.Configuration + store storage.Store + setters []clientSetter } func (h *handlerPolicyChange) Handle(ctx context.Context, a action, acker fleetAcker) error { @@ -31,9 +50,93 @@ func (h *handlerPolicyChange) Handle(ctx context.Context, a action, acker fleetA } h.log.Debugf("handlerPolicyChange: emit configuration for action %+v", a) + err = h.handleKibanaHosts(c) + if err != nil { + return err + } if err := h.emitter(c); err != nil { return err } return acker.Ack(ctx, action) } + +func (h *handlerPolicyChange) handleKibanaHosts(c *config.Config) (err error) { + cfg, err := configuration.NewFromConfig(c) + if err != nil { + return errors.New(err, "could not parse the configuration from the policy", errors.TypeConfig) + } + if kibanaEqual(h.config.Fleet.Kibana, cfg.Fleet.Kibana) { + // already the same hosts + return nil + } + + // only set protocol/hosts as that is all Fleet currently sends + prevProtocol := h.config.Fleet.Kibana.Protocol + prevHosts := h.config.Fleet.Kibana.Hosts + h.config.Fleet.Kibana.Protocol = cfg.Fleet.Kibana.Protocol + h.config.Fleet.Kibana.Hosts = cfg.Fleet.Kibana.Hosts + + // rollback on failure + defer func() { + if err != nil { + h.config.Fleet.Kibana.Protocol = prevProtocol + h.config.Fleet.Kibana.Hosts = prevHosts + } + }() + + client, err := fleetapi.NewAuthWithConfig(h.log, h.config.Fleet.AccessAPIKey, h.config.Fleet.Kibana) + if err != nil { + return errors.New( + err, "fail to create API client with updated hosts", + errors.TypeNetwork, errors.M("hosts", h.config.Fleet.Kibana.Hosts)) + } + reader, err := fleetToReader(h.agentInfo, h.config) + if err != nil { + return errors.New( + err, "fail to persist updated API client hosts", + errors.TypeUnexpected, errors.M("hosts", h.config.Fleet.Kibana.Hosts)) + } + err = h.store.Save(reader) + if err != nil { + return errors.New( + err, "fail to persist updated API client hosts", + errors.TypeFilesystem, errors.M("hosts", h.config.Fleet.Kibana.Hosts)) + } + for _, setter := range h.setters { + setter.SetClient(client) + } + return nil +} + +func kibanaEqual(k1 *kibana.Config, k2 *kibana.Config) bool { + if k1.Protocol != k2.Protocol { + return false + } + + sort.Strings(k1.Hosts) + sort.Strings(k2.Hosts) + if len(k1.Hosts) != len(k2.Hosts) { + return false + } + for i, v := range k1.Hosts { + if v != k2.Hosts[i] { + return false + } + } + return true +} + +func fleetToReader(agentInfo *info.AgentInfo, cfg *configuration.Configuration) (io.Reader, error) { + configToStore := map[string]interface{}{ + "fleet": cfg.Fleet, + "agent": map[string]interface{}{ + "id": agentInfo.AgentID(), + }, + } + data, err := yaml.Marshal(configToStore) + if err != nil { + return nil, err + } + return bytes.NewReader(data), nil +} diff --git a/x-pack/elastic-agent/pkg/agent/application/handler_action_policy_change_test.go b/x-pack/elastic-agent/pkg/agent/application/handler_action_policy_change_test.go index ce4802b68e6..c30f886b0d1 100644 --- a/x-pack/elastic-agent/pkg/agent/application/handler_action_policy_change_test.go +++ b/x-pack/elastic-agent/pkg/agent/application/handler_action_policy_change_test.go @@ -9,6 +9,10 @@ import ( "sync" "testing" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/info" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/configuration" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/storage" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -31,6 +35,8 @@ func (m *mockEmitter) Emitter(policy *config.Config) error { func TestPolicyChange(t *testing.T) { log, _ := logger.New("") ack := newNoopAcker() + agentInfo, _ := info.NewAgentInfo() + nullStore := &storage.NullStore{} t.Run("Receive a config change and successfully emits a raw configuration", func(t *testing.T) { emitter := &mockEmitter{} @@ -42,7 +48,14 @@ func TestPolicyChange(t *testing.T) { Policy: conf, } - handler := &handlerPolicyChange{log: log, emitter: emitter.Emitter} + cfg := configuration.DefaultConfiguration() + handler := &handlerPolicyChange{ + log: log, + emitter: emitter.Emitter, + agentInfo: agentInfo, + config: cfg, + store: nullStore, + } err := handler.Handle(context.Background(), action, ack) require.NoError(t, err) @@ -60,7 +73,14 @@ func TestPolicyChange(t *testing.T) { Policy: conf, } - handler := &handlerPolicyChange{log: log, emitter: emitter.Emitter} + cfg := configuration.DefaultConfiguration() + handler := &handlerPolicyChange{ + log: log, + emitter: emitter.Emitter, + agentInfo: agentInfo, + config: cfg, + store: nullStore, + } err := handler.Handle(context.Background(), action, ack) require.Error(t, err) @@ -69,6 +89,9 @@ func TestPolicyChange(t *testing.T) { func TestPolicyAcked(t *testing.T) { log, _ := logger.New("") + agentInfo, _ := info.NewAgentInfo() + nullStore := &storage.NullStore{} + t.Run("Config change should not ACK on error", func(t *testing.T) { tacker := &testAcker{} @@ -83,7 +106,14 @@ func TestPolicyAcked(t *testing.T) { Policy: config, } - handler := &handlerPolicyChange{log: log, emitter: emitter.Emitter} + cfg := configuration.DefaultConfiguration() + handler := &handlerPolicyChange{ + log: log, + emitter: emitter.Emitter, + agentInfo: agentInfo, + config: cfg, + store: nullStore, + } err := handler.Handle(context.Background(), action, tacker) require.Error(t, err) @@ -105,7 +135,14 @@ func TestPolicyAcked(t *testing.T) { Policy: config, } - handler := &handlerPolicyChange{log: log, emitter: emitter.Emitter} + cfg := configuration.DefaultConfiguration() + handler := &handlerPolicyChange{ + log: log, + emitter: emitter.Emitter, + agentInfo: agentInfo, + config: cfg, + store: nullStore, + } err := handler.Handle(context.Background(), action, tacker) require.NoError(t, err) diff --git a/x-pack/elastic-agent/pkg/agent/application/managed_mode.go b/x-pack/elastic-agent/pkg/agent/application/managed_mode.go index 647eae6d4e6..e38685741c3 100644 --- a/x-pack/elastic-agent/pkg/agent/application/managed_mode.go +++ b/x-pack/elastic-agent/pkg/agent/application/managed_mode.go @@ -209,12 +209,17 @@ func newManaged( acker, combinedReporter) + policyChanger := &handlerPolicyChange{ + log: log, + emitter: emit, + agentInfo: agentInfo, + config: cfg, + store: store, + setters: []clientSetter{acker}, + } actionDispatcher.MustRegister( &fleetapi.ActionPolicyChange{}, - &handlerPolicyChange{ - log: log, - emitter: emit, - }, + policyChanger, ) actionDispatcher.MustRegister( @@ -264,6 +269,9 @@ func newManaged( if err != nil { return nil, err } + // add the gateway to setters, so the gateway can be updated + // when the hosts for Kibana are updated by the policy. + policyChanger.setters = append(policyChanger.setters, gateway) managedApplication.gateway = gateway return managedApplication, nil diff --git a/x-pack/elastic-agent/pkg/agent/application/managed_mode_test.go b/x-pack/elastic-agent/pkg/agent/application/managed_mode_test.go index 65cb27547ff..4325c5ce7a6 100644 --- a/x-pack/elastic-agent/pkg/agent/application/managed_mode_test.go +++ b/x-pack/elastic-agent/pkg/agent/application/managed_mode_test.go @@ -9,11 +9,14 @@ import ( "encoding/json" "testing" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/configuration" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/info" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/configrequest" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/storage" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/composable" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/fleetapi" @@ -34,6 +37,7 @@ func TestManagedModeRouting(t *testing.T) { log, _ := logger.New("") router, _ := newRouter(log, streamFn) agentInfo, _ := info.NewAgentInfo() + nullStore := &storage.NullStore{} composableCtrl, _ := composable.New(log, nil) emit, err := emitter(ctx, log, agentInfo, composableCtrl, router, &configModifiers{Decorators: []decoratorFunc{injectMonitoring}}) require.NoError(t, err) @@ -41,11 +45,15 @@ func TestManagedModeRouting(t *testing.T) { actionDispatcher, err := newActionDispatcher(ctx, log, &handlerDefault{log: log}) require.NoError(t, err) + cfg := configuration.DefaultConfiguration() actionDispatcher.MustRegister( &fleetapi.ActionPolicyChange{}, &handlerPolicyChange{ - log: log, - emitter: emit, + log: log, + emitter: emit, + agentInfo: agentInfo, + config: cfg, + store: nullStore, }, ) diff --git a/x-pack/elastic-agent/pkg/agent/storage/storage.go b/x-pack/elastic-agent/pkg/agent/storage/storage.go index b37c6e06ab3..2435311486a 100644 --- a/x-pack/elastic-agent/pkg/agent/storage/storage.go +++ b/x-pack/elastic-agent/pkg/agent/storage/storage.go @@ -20,7 +20,9 @@ import ( const perms os.FileMode = 0600 -type store interface { +// Store saves the io.Reader. +type Store interface { + // Save the io.Reader. Save(io.Reader) error } @@ -62,12 +64,12 @@ type ReplaceOnSuccessStore struct { target string replaceWith []byte - wrapped store + wrapped Store } // NewReplaceOnSuccessStore takes a target file and a replacement content and will replace the target // file content if the wrapped store execution is done without any error. -func NewReplaceOnSuccessStore(target string, replaceWith []byte, wrapped store) *ReplaceOnSuccessStore { +func NewReplaceOnSuccessStore(target string, replaceWith []byte, wrapped Store) *ReplaceOnSuccessStore { return &ReplaceOnSuccessStore{ target: target, replaceWith: replaceWith, From 53892413e05b63d588e728d3252d145bb4247a59 Mon Sep 17 00:00:00 2001 From: Ivan Fernandez Calvo Date: Thu, 8 Oct 2020 09:54:42 +0200 Subject: [PATCH 55/93] chore: add versions 7.1x (#21670) --- .ci/jobs/apm-beats-update.yml | 5 ++++- .ci/jobs/beats.yml | 5 ++++- .ci/jobs/packaging.yml | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.ci/jobs/apm-beats-update.yml b/.ci/jobs/apm-beats-update.yml index 2ae688ffab7..678a9fbcd67 100644 --- a/.ci/jobs/apm-beats-update.yml +++ b/.ci/jobs/apm-beats-update.yml @@ -18,7 +18,7 @@ discover-pr-forks-trust: 'permission' discover-pr-origin: 'merge-current' discover-tags: true - head-filter-regex: '(master|7\.[x789]|8\.\d+|PR-.*|v\d+\.\d+\.\d+)' + head-filter-regex: '(master|7\.[x789]|7\.1\d|8\.\d+|PR-.*|v\d+\.\d+\.\d+)' disable-pr-notifications: true notification-context: 'apm-beats-update' repo: 'beats' @@ -38,6 +38,9 @@ - regex-name: regex: '7\.[x789]' case-sensitive: true + - regex-name: + regex: '7\.1\d' + case-sensitive: true - regex-name: regex: '8\.\d+' case-sensitive: true diff --git a/.ci/jobs/beats.yml b/.ci/jobs/beats.yml index 6f59a9bcdf8..27095b2fecb 100644 --- a/.ci/jobs/beats.yml +++ b/.ci/jobs/beats.yml @@ -17,7 +17,7 @@ discover-pr-forks-strategy: 'merge-current' discover-pr-forks-trust: 'permission' discover-pr-origin: 'merge-current' - head-filter-regex: '(master|7\.[x789]|8\.\d+|PR-.*|v\d+\.\d+\.\d+)' + head-filter-regex: '(master|7\.[x789]|7\.1\d|8\.\d+|PR-.*|v\d+\.\d+\.\d+)' discover-tags: true notification-context: "beats-ci" repo: 'beats' @@ -38,6 +38,9 @@ - regex-name: regex: '7\.[x789]' case-sensitive: true + - regex-name: + regex: '7\.1\d' + case-sensitive: true - regex-name: regex: '8\.\d+' case-sensitive: true diff --git a/.ci/jobs/packaging.yml b/.ci/jobs/packaging.yml index fd6fb9f90c6..baa3ce45035 100644 --- a/.ci/jobs/packaging.yml +++ b/.ci/jobs/packaging.yml @@ -14,7 +14,7 @@ discover-pr-forks-trust: 'permission' discover-pr-origin: 'merge-current' discover-tags: true - head-filter-regex: '(master|7\.[x789]|8\.\d+|PR-.*|v\d+\.\d+\.\d+)' + head-filter-regex: '(master|7\.[x789]|7\.1\d|8\.\d+|PR-.*|v\d+\.\d+\.\d+)' disable-pr-notifications: true notification-context: 'beats-packaging' repo: 'beats' @@ -34,6 +34,9 @@ - regex-name: regex: '7\.[x789]' case-sensitive: true + - regex-name: + regex: '7\.1\d' + case-sensitive: true - regex-name: regex: '8\.\d+' case-sensitive: true From 47546025421fa6cfb3945799968313b18ad82783 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9mi=20V=C3=A1nyi?= Date: Thu, 8 Oct 2020 11:51:10 +0200 Subject: [PATCH 56/93] Fix flaky FSWatch/FSScanner tests (#21625) ## What does this PR do? Unit tests in for `fswatch.go` do not depend on the order of the returned events anymore. Closes #21489 --- filebeat/input/filestream/fswatch_test.go | 32 ++++--------------- .../filestream/fswatch_test_non_windows.go | 2 +- 2 files changed, 8 insertions(+), 26 deletions(-) diff --git a/filebeat/input/filestream/fswatch_test.go b/filebeat/input/filestream/fswatch_test.go index d6286a273eb..2435fad1500 100644 --- a/filebeat/input/filestream/fswatch_test.go +++ b/filebeat/input/filestream/fswatch_test.go @@ -38,8 +38,6 @@ var ( ) func TestFileScanner(t *testing.T) { - t.Skip("Flaky test: https://github.com/elastic/beats/issues/21489") - testCases := map[string]struct { paths []string excludedFiles []match.Matcher @@ -89,7 +87,7 @@ func TestFileScanner(t *testing.T) { for p, _ := range files { paths = append(paths, p) } - assert.True(t, checkIfSameContents(test.expectedFiles, paths)) + assert.ElementsMatch(t, paths, test.expectedFiles) }) } } @@ -116,26 +114,7 @@ func removeFilesOfScannerTest(t *testing.T) { } } -// only handles sets -func checkIfSameContents(one, other []string) bool { - if len(one) != len(other) { - return false - } - - mustFind := len(one) - for _, oneElem := range one { - for _, otherElem := range other { - if oneElem == otherElem { - mustFind-- - } - } - } - return mustFind == 0 -} - func TestFileWatchNewDeleteModified(t *testing.T) { - t.Skip("Flaky test: https://github.com/elastic/beats/issues/21489") - oldTs := time.Now() newTs := oldTs.Add(5 * time.Second) testCases := map[string]struct { @@ -226,10 +205,13 @@ func TestFileWatchNewDeleteModified(t *testing.T) { go w.watch(context.Background()) - for _, expectedEvent := range test.expectedEvents { - evt := w.Event() - assert.Equal(t, expectedEvent, evt) + count := len(test.expectedEvents) + actual := make([]loginp.FSEvent, count) + for i := 0; i < count; i++ { + actual[i] = w.Event() } + + assert.ElementsMatch(t, actual, test.expectedEvents) }) } } diff --git a/filebeat/input/filestream/fswatch_test_non_windows.go b/filebeat/input/filestream/fswatch_test_non_windows.go index eecfeddf930..3c316efdfc9 100644 --- a/filebeat/input/filestream/fswatch_test_non_windows.go +++ b/filebeat/input/filestream/fswatch_test_non_windows.go @@ -88,7 +88,7 @@ func TestFileScannerSymlinks(t *testing.T) { for p, _ := range files { paths = append(paths, p) } - assert.Equal(t, test.expectedFiles, paths) + assert.ElementsMatch(t, test.expectedFiles, paths) }) } } From 1f29969264b64cbcfa09df86ffc228161d4d2e3f Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Thu, 8 Oct 2020 10:57:41 +0100 Subject: [PATCH 57/93] Docker build resiliance with a retry (#21587) --- dev-tools/mage/integtest_docker.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dev-tools/mage/integtest_docker.go b/dev-tools/mage/integtest_docker.go index 2ed09db711e..8b366bad315 100644 --- a/dev-tools/mage/integtest_docker.go +++ b/dev-tools/mage/integtest_docker.go @@ -27,6 +27,7 @@ import ( "runtime" "strings" "sync" + "time" "github.com/pkg/errors" @@ -246,5 +247,17 @@ func dockerComposeBuildImages() error { os.Stderr, "docker-compose", args..., ) + + // This sleep is to avoid hitting the docker build issues when resources are not available. + if err != nil { + fmt.Println(">> Building docker images again") + time.Sleep(10) + _, err = sh.Exec( + composeEnv, + out, + os.Stderr, + "docker-compose", args..., + ) + } return err } From f344a6769eaa04c6f3fc960551b5b8a8fa8100c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 8 Oct 2020 13:16:16 +0200 Subject: [PATCH 58/93] [CI: Packaging] fix: push ubi8 images too (#21621) * fix: push ubi8 images too * chore: enhance retries Co-authored-by: Victor Martinez * chore: use variables in log * chore: add "-oss" images Co-authored-by: Victor Martinez --- .ci/packaging.groovy | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/.ci/packaging.groovy b/.ci/packaging.groovy index 26916372b70..81ff5b1994a 100644 --- a/.ci/packaging.groovy +++ b/.ci/packaging.groovy @@ -198,17 +198,30 @@ def tagAndPush(name){ tagName = "pr-${env.CHANGE_ID}" } - def oldName = "${DOCKER_REGISTRY}/beats/${name}:${libbetaVer}" - def newName = "${DOCKER_REGISTRY}/observability-ci/${name}:${tagName}" - def commitName = "${DOCKER_REGISTRY}/observability-ci/${name}:${env.GIT_BASE_COMMIT}" dockerLogin(secret: "${DOCKERELASTIC_SECRET}", registry: "${DOCKER_REGISTRY}") - retry(3){ - sh(label:'Change tag and push', script: """ - docker tag ${oldName} ${newName} - docker push ${newName} - docker tag ${oldName} ${commitName} - docker push ${commitName} - """) + + // supported image flavours + def variants = ["", "-oss", "-ubi8"] + variants.each { variant -> + def oldName = "${DOCKER_REGISTRY}/beats/${name}${variant}:${libbetaVer}" + def newName = "${DOCKER_REGISTRY}/observability-ci/${name}${variant}:${tagName}" + def commitName = "${DOCKER_REGISTRY}/observability-ci/${name}${variant}:${env.GIT_BASE_COMMIT}" + + def iterations = 0 + retryWithSleep(retries: 3, seconds: 5, backoff: true) + iterations++ + def status = sh(label:'Change tag and push', script: """ + docker tag ${oldName} ${newName} + docker push ${newName} + docker tag ${oldName} ${commitName} + docker push ${commitName} + """, returnStatus: true) + if ( status > 0 && iterations < 3) { + error('tag and push failed, retry') + } else if ( status > 0 ) { + log(level: 'WARN', text: "${name} doesn't have ${variant} docker images. See https://github.com/elastic/beats/pull/21621") + } + } } } From d5e89d961ade56b5edf1fe5059e15e64f6426071 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Thu, 8 Oct 2020 12:16:53 +0100 Subject: [PATCH 59/93] backport: add 7.10 branch (#21635) --- .backportrc.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.backportrc.json b/.backportrc.json index 3dce189d671..8bfd891160c 100644 --- a/.backportrc.json +++ b/.backportrc.json @@ -1,6 +1,6 @@ { "upstream": "elastic/beats", - "branches": [{ "name": "7.9"}, { "name": "7.8"}, { "name": "7.7"}, { "name": "7.x"}], + "branches": [ { "name": "7.x", "checked": true }, "7.10", "7.9", "7.8", "7.7"], "labels": ["backport"], "autoAssign": true, "prTitle": "Cherry-pick to {targetBranch}: {commitMessages}" From df03addd29f2d051ea54c55ba10273eaf44d2bfa Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Thu, 8 Oct 2020 14:10:28 +0200 Subject: [PATCH 60/93] Skip publisher flaky tests (#21657) --- libbeat/publisher/pipeline/controller_test.go | 8 +++++--- libbeat/publisher/pipeline/output_test.go | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/libbeat/publisher/pipeline/controller_test.go b/libbeat/publisher/pipeline/controller_test.go index fba98018894..1e400476a30 100644 --- a/libbeat/publisher/pipeline/controller_test.go +++ b/libbeat/publisher/pipeline/controller_test.go @@ -31,7 +31,8 @@ import ( "github.com/elastic/beats/v7/libbeat/publisher" "github.com/elastic/beats/v7/libbeat/publisher/queue" "github.com/elastic/beats/v7/libbeat/publisher/queue/memqueue" - "github.com/elastic/beats/v7/libbeat/tests/resources" + + //"github.com/elastic/beats/v7/libbeat/tests/resources" "github.com/stretchr/testify/require" ) @@ -46,8 +47,9 @@ func TestOutputReload(t *testing.T) { t.Run(name, func(t *testing.T) { testutil.SeedPRNG(t) - goroutines := resources.NewGoroutinesChecker() - defer goroutines.Check(t) + // Flaky check: https://github.com/elastic/beats/issues/21656 + //goroutines := resources.NewGoroutinesChecker() + //defer goroutines.Check(t) err := quick.Check(func(q uint) bool { numEventsToPublish := 15000 + (q % 500) // 15000 to 19999 diff --git a/libbeat/publisher/pipeline/output_test.go b/libbeat/publisher/pipeline/output_test.go index 5be34fa9436..7bde6e137ab 100644 --- a/libbeat/publisher/pipeline/output_test.go +++ b/libbeat/publisher/pipeline/output_test.go @@ -95,6 +95,8 @@ func TestMakeClientWorker(t *testing.T) { } func TestReplaceClientWorker(t *testing.T) { + t.Skip("Flaky test: https://github.com/elastic/beats/issues/17965") + tests := map[string]func(mockPublishFn) outputs.Client{ "client": newMockClient, "network_client": newMockNetworkClient, From 38c6a7026c5eaf419fc2f000f6b3b5cab9105f16 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Thu, 8 Oct 2020 14:08:57 +0100 Subject: [PATCH 61/93] [CI] Support Windows-2016 in pipeline 2.0 (#21337) --- .ci/jobs/beats-windows-mbp.yml | 56 -- .ci/windows.groovy | 874 --------------------------- auditbeat/Jenkinsfile.yml | 11 + filebeat/Jenkinsfile.yml | 1 + heartbeat/Jenkinsfile.yml | 11 + metricbeat/Jenkinsfile.yml | 11 + packetbeat/Jenkinsfile.yml | 11 + winlogbeat/Jenkinsfile.yml | 11 + x-pack/auditbeat/Jenkinsfile.yml | 11 + x-pack/elastic-agent/Jenkinsfile.yml | 11 + x-pack/filebeat/Jenkinsfile.yml | 11 + x-pack/functionbeat/Jenkinsfile.yml | 11 + x-pack/metricbeat/Jenkinsfile.yml | 11 + x-pack/packetbeat/Jenkinsfile.yml | 11 + x-pack/winlogbeat/Jenkinsfile.yml | 11 + 15 files changed, 133 insertions(+), 930 deletions(-) delete mode 100644 .ci/jobs/beats-windows-mbp.yml delete mode 100644 .ci/windows.groovy diff --git a/.ci/jobs/beats-windows-mbp.yml b/.ci/jobs/beats-windows-mbp.yml deleted file mode 100644 index 7ea26c9ba18..00000000000 --- a/.ci/jobs/beats-windows-mbp.yml +++ /dev/null @@ -1,56 +0,0 @@ ---- -- job: - name: Beats/beats-windows-mbp - display-name: Beats Pipeline for Windows - description: Jenkins pipeline for the Beats project running on windows agents. - view: Beats - project-type: multibranch - script-path: .ci/windows.groovy - scm: - - github: - branch-discovery: no-pr - discover-pr-forks-strategy: merge-current - discover-pr-forks-trust: permission - discover-pr-origin: merge-current - discover-tags: false - # Run MBP for the master branch and PRs - head-filter-regex: '(master|PR-.*)' - notification-context: 'beats-ci/windows' - repo: beats - repo-owner: elastic - credentials-id: github-app-beats-ci - ssh-checkout: - credentials: f6c7695a-671e-4f4f-a331-acdce44ff9ba - build-strategies: - - tags: - ignore-tags-older-than: -1 - ignore-tags-newer-than: -1 - - regular-branches: true - - change-request: - ignore-target-only-changes: true - property-strategies: - # Builds for PRs won't be triggered automatically. - # Only the master branch will be triggered automatically. - named-branches: - defaults: - - suppress-scm-triggering: true - exceptions: - - exception: - branch-name: master - properties: - - suppress-scm-triggering: false - clean: - after: true - before: true - prune: true - shallow-clone: true - depth: 10 - do-not-fetch-tags: true - submodule: - disable: false - recursive: true - parent-credentials: true - timeout: 100 - timeout: '15' - use-author: true - wipe-workspace: 'True' diff --git a/.ci/windows.groovy b/.ci/windows.groovy deleted file mode 100644 index 92826e80926..00000000000 --- a/.ci/windows.groovy +++ /dev/null @@ -1,874 +0,0 @@ -#!/usr/bin/env groovy - -@Library('apm@current') _ - -import groovy.transform.Field - -/** - This is required to store the stashed id with the test results to be digested with runbld -*/ -@Field def stashedTestReports = [:] - -/** - List of supported windows versions to be tested with - NOTE: - - 'windows-10' is too slow - - 'windows-2012-r2', 'windows-2008-r2', 'windows-7', 'windows-7-32-bit' are disabled - since we are working on releasing each windows version incrementally. -*/ -@Field def windowsVersions = ['windows-2019', 'windows-2016', 'windows-2012-r2'] - -pipeline { - agent { label 'ubuntu && immutable' } - environment { - BASE_DIR = 'src/github.com/elastic/beats' - GOX_FLAGS = "-arch amd64" - DOCKER_COMPOSE_VERSION = "1.21.0" - TERRAFORM_VERSION = "0.12.24" - PIPELINE_LOG_LEVEL = "INFO" - DOCKERELASTIC_SECRET = 'secret/observability-team/ci/docker-registry/prod' - DOCKER_REGISTRY = 'docker.elastic.co' - AWS_ACCOUNT_SECRET = 'secret/observability-team/ci/elastic-observability-aws-account-auth' - RUNBLD_DISABLE_NOTIFICATIONS = 'true' - JOB_GCS_BUCKET = 'beats-ci-temp' - JOB_GCS_CREDENTIALS = 'beats-ci-gcs-plugin' - } - options { - timeout(time: 2, unit: 'HOURS') - buildDiscarder(logRotator(numToKeepStr: '20', artifactNumToKeepStr: '20', daysToKeepStr: '30')) - timestamps() - ansiColor('xterm') - disableResume() - durabilityHint('PERFORMANCE_OPTIMIZED') - quietPeriod(10) - rateLimitBuilds(throttle: [count: 60, durationName: 'hour', userBoost: true]) - } - triggers { - issueCommentTrigger('(?i).*(?:jenkins\\W+)?run\\W+(?:the\\W+)?tests(?:\\W+please)?.*') - } - parameters { - booleanParam(name: 'runAllStages', defaultValue: false, description: 'Allow to run all stages.') - booleanParam(name: 'windowsTest', defaultValue: true, description: 'Allow Windows stages.') - booleanParam(name: 'macosTest', defaultValue: true, description: 'Allow macOS stages.') - - booleanParam(name: 'allCloudTests', defaultValue: false, description: 'Run all cloud integration tests.') - booleanParam(name: 'awsCloudTests', defaultValue: false, description: 'Run AWS cloud integration tests.') - string(name: 'awsRegion', defaultValue: 'eu-central-1', description: 'Default AWS region to use for testing.') - - booleanParam(name: 'debug', defaultValue: false, description: 'Allow debug logging for Jenkins steps') - booleanParam(name: 'dry_run', defaultValue: false, description: 'Skip build steps, it is for testing pipeline flow') - } - stages { - /** - Checkout the code and stash it, to use it on other stages. - */ - stage('Checkout') { - options { skipDefaultCheckout() } - steps { - pipelineManager([ cancelPreviousRunningBuilds: [ when: 'PR' ] ]) - deleteDir() - gitCheckout(basedir: "${BASE_DIR}", githubNotifyFirstTimeContributor: true) - stashV2(name: 'source', bucket: "${JOB_GCS_BUCKET}", credentialsId: "${JOB_GCS_CREDENTIALS}") - dir("${BASE_DIR}"){ - loadConfigEnvVars() - } - whenTrue(params.debug){ - dumpFilteredEnvironment() - } - } - } - stage('Lint'){ - options { skipDefaultCheckout() } - steps { - // NOTE: commented to run the windows pipeline a bit faster. - // when required then it can be enabled. - // makeTarget("Lint", "check") - echo 'SKIPPED' - } - } - stage('Build and Test Windows'){ - failFast false - parallel { - stage('Elastic Agent x-pack Windows'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - // NOTE: commented to run all the windows stages. - //return env.BUILD_ELASTIC_AGENT_XPACK != "false" && params.windowsTest - } - } - steps { - mageTargetWin("Elastic Agent x-pack Windows Unit test", "x-pack/elastic-agent", "build unitTest") - } - } - stage('Filebeat Windows'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - // NOTE: commented to run all the windows stages. - //return env.BUILD_FILEBEAT != "false" && params.windowsTest - } - } - steps { - mageTargetWin("Filebeat oss Windows Unit test", "filebeat", "build unitTest") - } - } - stage('Filebeat x-pack Windows'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - // NOTE: commented to run all the windows stages. - //return env.BUILD_FILEBEAT_XPACK != "false" && params.windowsTest - } - } - steps { - mageTargetWin("Filebeat x-pack Windows", "x-pack/filebeat", "build unitTest") - } - } - stage('Heartbeat'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - // NOTE: commented to run all the windows stages. - //return env.BUILD_HEARTBEAT != "false" - } - } - stages { - stage('Heartbeat Windows'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - } - } - steps { - mageTargetWin("Heartbeat oss Windows Unit test", "heartbeat", "build unitTest") - } - } - } - } - stage('Auditbeat oss Windows'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - // NOTE: commented to run all the windows stages. - //return env.BUILD_AUDITBEAT != "false" && params.windowsTest - } - } - steps { - mageTargetWin("Auditbeat oss Windows Unit test", "auditbeat", "build unitTest") - } - } - stage('Auditbeat x-pack Windows'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - // NOTE: commented to run all the windows stages. - //return env.BUILD_AUDITBEAT_XPACK != "false" && params.windowsTest - } - } - steps { - mageTargetWin("Auditbeat x-pack Windows", "x-pack/auditbeat", "build unitTest") - } - } - stage('Metricbeat Windows'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - // NOTE: commented to run all the windows stages. - //return env.BUILD_METRICBEAT != "false" && params.windowsTest - } - } - steps { - mageTargetWin("Metricbeat Windows Unit test", "metricbeat", "build unitTest") - } - } - stage('Metricbeat x-pack Windows'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - // NOTE: commented to run all the windows stages. - //return env.BUILD_METRICBEAT_XPACK != "false" && params.windowsTest - } - } - steps { - mageTargetWin("Metricbeat x-pack Windows", "x-pack/metricbeat", "build unitTest") - } - } - stage('Winlogbeat'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - // NOTE: commented to run all the windows stages. - //return env.BUILD_WINLOGBEAT != "false" - } - } - stages { - stage('Winlogbeat Windows'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - } - } - steps { - mageTargetWin("Winlogbeat Windows Unit test", "winlogbeat", "build unitTest") - } - } - } - } - stage('Winlogbeat Windows x-pack'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - // NOTE: commented to run all the windows stages. - //return params.windowsTest && env.BUILD_WINLOGBEAT_XPACK != "false" - } - } - steps { - mageTargetWin("Winlogbeat x-pack Windows", "x-pack/winlogbeat", "build unitTest") - } - } - stage('Functionbeat'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - // NOTE: commented to run all the windows stages. - //return env.BUILD_FUNCTIONBEAT_XPACK != "false" - } - } - stages { - stage('Functionbeat Windows x-pack'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - } - } - steps { - mageTargetWin("Functionbeat x-pack Windows Unit test", "x-pack/functionbeat", "build unitTest") - } - } - } - } - } - } - } - post { - always { - runbld() - } - cleanup { - notifyBuildResult(prComment: true) - } - } -} - -def delete() { - dir("${env.BASE_DIR}") { - fixPermissions("${WORKSPACE}") - } - deleteDir() -} - -def fixPermissions(location) { - sh(label: 'Fix permissions', script: """#!/usr/bin/env bash - source ./dev-tools/common.bash - docker_setup - script/fix_permissions.sh ${location}""", returnStatus: true) -} - -def makeTarget(String context, String target, boolean clean = true) { - withGithubNotify(context: "${context}") { - withBeatsEnv(true) { - whenTrue(params.debug) { - dumpFilteredEnvironment() - dumpMage() - } - sh(label: "Make ${target}", script: "make ${target}") - whenTrue(clean) { - fixPermissions("${HOME}") - } - } - } -} - -def mageTarget(String context, String directory, String target) { - withGithubNotify(context: "${context}") { - withBeatsEnv(true) { - whenTrue(params.debug) { - dumpFilteredEnvironment() - dumpMage() - } - - def verboseFlag = params.debug ? "-v" : "" - dir(directory) { - sh(label: "Mage ${target}", script: "mage ${verboseFlag} ${target}") - } - } - } -} - -def mageTargetWin(String context, String directory, String target) { - withGithubNotify(context: "${context}") { - def tasks = [:] - windowsVersions.each { os -> - tasks["${context}-${os}"] = mageTargetWin(context, directory, target, os) - } - parallel(tasks) - } -} - -def mageTargetWin(String context, String directory, String target, String label) { - return { - log(level: 'INFO', text: "context=${context} directory=${directory} target=${target} os=${label}") - def immutable = label.equals('windows-7-32-bit') ? 'windows-immutable-32-bit' : 'windows-immutable' - - // NOTE: skip filebeat with windows-2016/2012-r2 since there are some test failures. - // See https://github.com/elastic/beats/issues/19787 https://github.com/elastic/beats/issues/19641 - if (directory.equals('filebeat') && (label.equals('windows-2016') || label.equals('windows-2012-r2'))) { - log(level: 'WARN', text: "Skipped stage for the 'filebeat' with '${label}' as long as there are test failures to be analysed.") - } else { - node("${immutable} && ${label}"){ - withBeatsEnvWin() { - whenTrue(params.debug) { - dumpFilteredEnvironment() - dumpMageWin() - } - - def verboseFlag = params.debug ? "-v" : "" - dir(directory) { - bat(label: "Mage ${target}", script: "mage ${verboseFlag} ${target}") - } - } - } - } - } -} - -def withBeatsEnv(boolean archive, Closure body) { - def os = goos() - def goRoot = "${env.WORKSPACE}/.gvm/versions/go${GO_VERSION}.${os}.amd64" - - withEnv([ - "HOME=${env.WORKSPACE}", - "GOPATH=${env.WORKSPACE}", - "GOROOT=${goRoot}", - "PATH=${env.WORKSPACE}/bin:${goRoot}/bin:${env.PATH}", - "MAGEFILE_CACHE=${WORKSPACE}/.magefile", - "TEST_COVERAGE=true", - "RACE_DETECTOR=true", - "PYTHON_ENV=${WORKSPACE}/python-env", - "TEST_TAGS=${env.TEST_TAGS},oracle", - "DOCKER_PULL=0", - ]) { - deleteDir() - unstashV2(name: 'source', bucket: "${JOB_GCS_BUCKET}", credentialsId: "${JOB_GCS_CREDENTIALS}") - if(isDockerInstalled()){ - dockerLogin(secret: "${DOCKERELASTIC_SECRET}", registry: "${DOCKER_REGISTRY}") - } - dir("${env.BASE_DIR}") { - installTools() - // TODO (2020-04-07): This is a work-around to fix the Beat generator tests. - // See https://github.com/elastic/beats/issues/17787. - setGitConfig() - try { - if(!params.dry_run){ - body() - } - } finally { - if (archive) { - catchError(buildResult: 'SUCCESS', stageResult: 'UNSTABLE') { - junitAndStore(allowEmptyResults: true, keepLongStdio: true, testResults: "**/build/TEST*.xml") - archiveArtifacts(allowEmptyArchive: true, artifacts: '**/build/TEST*.out') - } - } - reportCoverage() - } - } - } -} - -def withBeatsEnvWin(Closure body) { - final String chocoPath = 'C:\\ProgramData\\chocolatey\\bin' - final String chocoPython3Path = 'C:\\Python38;C:\\Python38\\Scripts' - // NOTE: to support Windows 7 32 bits the arch in the go context path is required. - def arch = is32bit() ? '386' : 'amd64' - def goRoot = "${env.USERPROFILE}\\.gvm\\versions\\go${GO_VERSION}.windows.${arch}" - - withEnv([ - "HOME=${env.WORKSPACE}", - "DEV_ARCH=${arch}", - "DEV_OS=windows", - "GOPATH=${env.WORKSPACE}", - "GOROOT=${goRoot}", - "PATH=${env.WORKSPACE}\\bin;${goRoot}\\bin;${chocoPath};${chocoPython3Path};C:\\tools\\mingw64\\bin;${env.PATH}", - "MAGEFILE_CACHE=${env.WORKSPACE}\\.magefile", - "TEST_COVERAGE=true", - "RACE_DETECTOR=true", - ]){ - deleteDir() - unstashV2(name: 'source', bucket: "${JOB_GCS_BUCKET}", credentialsId: "${JOB_GCS_CREDENTIALS}") - dir("${env.BASE_DIR}"){ - installTools() - try { - if(!params.dry_run){ - body() - } - } finally { - catchError(buildResult: 'SUCCESS', stageResult: 'UNSTABLE') { - junitAndStore(allowEmptyResults: true, keepLongStdio: true, testResults: "**\\build\\TEST*.xml") - archiveArtifacts(allowEmptyArchive: true, artifacts: '**\\build\\TEST*.out') - } - } - } - } -} - -def installTools() { - def i = 2 // Number of retries - if(isUnix()) { - retry(i) { sh(label: "Install Go ${GO_VERSION}", script: ".ci/scripts/install-go.sh") } - retry(i) { sh(label: "Install docker-compose ${DOCKER_COMPOSE_VERSION}", script: ".ci/scripts/install-docker-compose.sh") } - retry(i) { sh(label: "Install Terraform ${TERRAFORM_VERSION}", script: ".ci/scripts/install-terraform.sh") } - retry(i) { sh(label: "Install Mage", script: "make mage") } - } else { - retry(i) { bat(label: "Install Go/Mage/Python ${GO_VERSION}", script: ".ci/scripts/install-tools.bat") } - } -} - -def is32bit(){ - def labels = env.NODE_LABELS - return labels.contains('i386') -} - -def goos(){ - def labels = env.NODE_LABELS - - if (labels.contains('linux')) { - return 'linux' - } else if (labels.contains('windows')) { - return 'windows' - } else if (labels.contains('darwin')) { - return 'darwin' - } - - error("Unhandled OS name in NODE_LABELS: " + labels) -} - -def dumpMage(){ - echo "### MAGE DUMP ###" - sh(label: "Dump mage variables", script: "mage dumpVariables") - echo "### END MAGE DUMP ###" -} - -def dumpMageWin(){ - echo "### MAGE DUMP ###" - bat(label: "Dump mage variables", script: "mage dumpVariables") - echo "### END MAGE DUMP ###" -} - -def dumpFilteredEnvironment(){ - echo "### ENV DUMP ###" - echo "PATH: ${env.PATH}" - echo "HOME: ${env.HOME}" - echo "USERPROFILE: ${env.USERPROFILE}" - echo "BUILD_DIR: ${env.BUILD_DIR}" - echo "COVERAGE_DIR: ${env.COVERAGE_DIR}" - echo "BEATS: ${env.BEATS}" - echo "PROJECTS: ${env.PROJECTS}" - echo "PROJECTS_ENV: ${env.PROJECTS_ENV}" - echo "PYTHON_ENV: ${env.PYTHON_ENV}" - echo "PYTHON_EXE: ${env.PYTHON_EXE}" - echo "PYTHON_ENV_EXE: ${env.PYTHON_ENV_EXE}" - echo "VENV_PARAMS: ${env.VENV_PARAMS}" - echo "FIND: ${env.FIND}" - echo "GOLINT: ${env.GOLINT}" - echo "GOLINT_REPO: ${env.GOLINT_REPO}" - echo "REVIEWDOG: ${env.REVIEWDOG}" - echo "REVIEWDOG_OPTIONS: ${env.REVIEWDOG_OPTIONS}" - echo "REVIEWDOG_REPO: ${env.REVIEWDOG_REPO}" - echo "XPACK_SUFFIX: ${env.XPACK_SUFFIX}" - echo "PKG_BUILD_DIR: ${env.PKG_BUILD_DIR}" - echo "PKG_UPLOAD_DIR: ${env.PKG_UPLOAD_DIR}" - echo "COVERAGE_TOOL: ${env.COVERAGE_TOOL}" - echo "COVERAGE_TOOL_REPO: ${env.COVERAGE_TOOL_REPO}" - echo "TESTIFY_TOOL_REPO: ${env.TESTIFY_TOOL_REPO}" - echo "NOW: ${env.NOW}" - echo "GOBUILD_FLAGS: ${env.GOBUILD_FLAGS}" - echo "GOIMPORTS: ${env.GOIMPORTS}" - echo "GOIMPORTS_REPO: ${env.GOIMPORTS_REPO}" - echo "GOIMPORTS_LOCAL_PREFIX: ${env.GOIMPORTS_LOCAL_PREFIX}" - echo "PROCESSES: ${env.PROCESSES}" - echo "TIMEOUT: ${env.TIMEOUT}" - echo "PYTHON_TEST_FILES: ${env.PYTHON_TEST_FILES}" - echo "PYTEST_ADDOPTS: ${env.PYTEST_ADDOPTS}" - echo "PYTEST_OPTIONS: ${env.PYTEST_OPTIONS}" - echo "TEST_ENVIRONMENT: ${env.TEST_ENVIRONMENT}" - echo "SYSTEM_TESTS: ${env.SYSTEM_TESTS}" - echo "STRESS_TESTS: ${env.STRESS_TESTS}" - echo "STRESS_TEST_OPTIONS: ${env.STRESS_TEST_OPTIONS}" - echo "TEST_TAGS: ${env.TEST_TAGS}" - echo "GOX_OS: ${env.GOX_OS}" - echo "GOX_OSARCH: ${env.GOX_OSARCH}" - echo "GOX_FLAGS: ${env.GOX_FLAGS}" - echo "TESTING_ENVIRONMENT: ${env.TESTING_ENVIRONMENT}" - echo "BEAT_VERSION: ${env.BEAT_VERSION}" - echo "COMMIT_ID: ${env.COMMIT_ID}" - echo "DOCKER_COMPOSE_PROJECT_NAME: ${env.DOCKER_COMPOSE_PROJECT_NAME}" - echo "DOCKER_COMPOSE: ${env.DOCKER_COMPOSE}" - echo "DOCKER_CACHE: ${env.DOCKER_CACHE}" - echo "GOPACKAGES_COMMA_SEP: ${env.GOPACKAGES_COMMA_SEP}" - echo "PIP_INSTALL_PARAMS: ${env.PIP_INSTALL_PARAMS}" - echo "### END ENV DUMP ###" -} - -def k8sTest(versions){ - versions.each{ v -> - stage("k8s ${v}"){ - withEnv(["K8S_VERSION=${v}", "KIND_VERSION=v0.7.0", "KUBECONFIG=${env.WORKSPACE}/kubecfg"]){ - withGithubNotify(context: "K8s ${v}") { - withBeatsEnv(false) { - sh(label: "Install kind", script: ".ci/scripts/install-kind.sh") - sh(label: "Install kubectl", script: ".ci/scripts/install-kubectl.sh") - sh(label: "Setup kind", script: ".ci/scripts/kind-setup.sh") - sh(label: "Integration tests", script: "MODULE=kubernetes make -C metricbeat integration-tests") - sh(label: "Deploy to kubernetes",script: "make -C deploy/kubernetes test") - sh(label: 'Delete cluster', script: 'kind delete cluster') - } - } - } - } - } -} - -def reportCoverage(){ - catchError(buildResult: 'SUCCESS', stageResult: 'UNSTABLE') { - retry(2){ - sh(label: 'Report to Codecov', script: ''' - curl -sSLo codecov https://codecov.io/bash - for i in auditbeat filebeat heartbeat libbeat metricbeat packetbeat winlogbeat journalbeat - do - FILE="${i}/build/coverage/full.cov" - if [ -f "${FILE}" ]; then - bash codecov -f "${FILE}" - fi - done - ''') - } - } -} - -// isChanged treats the patterns as regular expressions. In order to check if -// any file in a directoy is modified use `^/.*`. -def isChanged(patterns){ - return ( - params.runAllStages - || isGitRegionMatch(patterns: patterns, comparator: 'regexp') - ) -} - -def isChangedOSSCode(patterns) { - def allPatterns = [ - "^Jenkinsfile", - "^go.mod", - "^libbeat/.*", - "^testing/.*", - "^dev-tools/.*", - "^\\.ci/scripts/.*", - ] - allPatterns.addAll(patterns) - return isChanged(allPatterns) -} - -def isChangedXPackCode(patterns) { - def allPatterns = [ - "^Jenkinsfile", - "^go.mod", - "^libbeat/.*", - "^dev-tools/.*", - "^testing/.*", - "^x-pack/libbeat/.*", - "^\\.ci/scripts/.*", - ] - allPatterns.addAll(patterns) - return isChanged(allPatterns) -} - -// withCloudTestEnv executes a closure with credentials for cloud test -// environments. -def withCloudTestEnv(Closure body) { - def maskedVars = [] - def testTags = "${env.TEST_TAGS}" - - // AWS - if (params.allCloudTests || params.awsCloudTests) { - testTags = "${testTags},aws" - def aws = getVaultSecret(secret: "${AWS_ACCOUNT_SECRET}").data - if (!aws.containsKey('access_key')) { - error("${AWS_ACCOUNT_SECRET} doesn't contain 'access_key'") - } - if (!aws.containsKey('secret_key')) { - error("${AWS_ACCOUNT_SECRET} doesn't contain 'secret_key'") - } - maskedVars.addAll([ - [var: "AWS_REGION", password: params.awsRegion], - [var: "AWS_ACCESS_KEY_ID", password: aws.access_key], - [var: "AWS_SECRET_ACCESS_KEY", password: aws.secret_key], - ]) - } - - withEnv([ - "TEST_TAGS=${testTags}", - ]) { - withEnvMask(vars: maskedVars) { - body() - } - } -} - -def terraformInit(String directory) { - dir(directory) { - sh(label: "Terraform Init on ${directory}", script: "terraform init") - } -} - -def terraformApply(String directory) { - terraformInit(directory) - dir(directory) { - sh(label: "Terraform Apply on ${directory}", script: "terraform apply -auto-approve") - } -} - -// Start testing environment on cloud using terraform. Terraform files are -// stashed so they can be used by other stages. They are also archived in -// case manual cleanup is needed. -// -// Example: -// startCloudTestEnv('x-pack-metricbeat', [ -// [cond: params.awsCloudTests, dir: 'x-pack/metricbeat/module/aws'], -// ]) -// ... -// terraformCleanup('x-pack-metricbeat', 'x-pack/metricbeat') -def startCloudTestEnv(String name, environments = []) { - withCloudTestEnv() { - withBeatsEnv(false) { - def runAll = params.runAllCloudTests - try { - for (environment in environments) { - if (environment.cond || runAll) { - retry(2) { - terraformApply(environment.dir) - } - } - } - } finally { - // Archive terraform states in case manual cleanup is needed. - archiveArtifacts(allowEmptyArchive: true, artifacts: '**/terraform.tfstate') - } - stash(name: "terraform-${name}", allowEmpty: true, includes: '**/terraform.tfstate,**/.terraform/**') - } - } -} - - -// Looks for all terraform states in directory and runs terraform destroy for them, -// it uses terraform states previously stashed by startCloudTestEnv. -def terraformCleanup(String stashName, String directory) { - stage("Remove cloud scenarios in ${directory}"){ - withCloudTestEnv() { - withBeatsEnv(false) { - unstash("terraform-${stashName}") - retry(2) { - sh(label: "Terraform Cleanup", script: ".ci/scripts/terraform-cleanup.sh ${directory}") - } - } - } - } -} - -def loadConfigEnvVars(){ - def empty = [] - env.GO_VERSION = readFile(".go-version").trim() - - withEnv(["HOME=${env.WORKSPACE}"]) { - retry(2) { sh(label: "Install Go ${env.GO_VERSION}", script: ".ci/scripts/install-go.sh") } - } - - // Libbeat is the core framework of Beats. It has no additional dependencies - // on other projects in the Beats repository. - env.BUILD_LIBBEAT = isChangedOSSCode(empty) - env.BUILD_LIBBEAT_XPACK = isChangedXPackCode(empty) - - // Auditbeat depends on metricbeat as framework, but does not include any of - // the modules from Metricbeat. - // The Auditbeat x-pack build contains all functionality from OSS Auditbeat. - env.BUILD_AUDITBEAT = isChangedOSSCode(getVendorPatterns('auditbeat')) - env.BUILD_AUDITBEAT_XPACK = isChangedXPackCode(getVendorPatterns('x-pack/auditbeat')) - - // Dockerlogbeat is a standalone Beat that only relies on libbeat. - env.BUILD_DOCKERLOGBEAT_XPACK = isChangedXPackCode(getVendorPatterns('x-pack/dockerlogbeat')) - - // Filebeat depends on libbeat only. - // The Filebeat x-pack build contains all functionality from OSS Filebeat. - env.BUILD_FILEBEAT = isChangedOSSCode(getVendorPatterns('filebeat')) - env.BUILD_FILEBEAT_XPACK = isChangedXPackCode(getVendorPatterns('x-pack/filebeat')) - - // Metricbeat depends on libbeat only. - // The Metricbeat x-pack build contains all functionality from OSS Metricbeat. - env.BUILD_METRICBEAT = isChangedOSSCode(getVendorPatterns('metricbeat')) - env.BUILD_METRICBEAT_XPACK = isChangedXPackCode(getVendorPatterns('x-pack/metricbeat')) - - // Functionbeat is a standalone beat that depends on libbeat only. - // Functionbeat is available as x-pack build only. - env.BUILD_FUNCTIONBEAT_XPACK = isChangedXPackCode(getVendorPatterns('x-pack/functionbeat')) - - // Heartbeat depends on libbeat only. - // The Heartbeat x-pack build contains all functionality from OSS Heartbeat. - env.BUILD_HEARTBEAT = isChangedOSSCode(getVendorPatterns('heartbeat')) - env.BUILD_HEARTBEAT_XPACK = isChangedXPackCode(getVendorPatterns('x-pack/heartbeat')) - - // Journalbeat depends on libbeat only. - // The Journalbeat x-pack build contains all functionality from OSS Journalbeat. - env.BUILD_JOURNALBEAT = isChangedOSSCode(getVendorPatterns('journalbeat')) - env.BUILD_JOURNALBEAT_XPACK = isChangedXPackCode(getVendorPatterns('x-pack/journalbeat')) - - // Packetbeat depends on libbeat only. - // The Packetbeat x-pack build contains all functionality from OSS Packetbeat. - env.BUILD_PACKETBEAT = isChangedOSSCode(getVendorPatterns('packetbeat')) - env.BUILD_PACKETBEAT_XPACK = isChangedXPackCode(getVendorPatterns('x-pack/packetbeat')) - - // Winlogbeat depends on libbeat only. - // The Winlogbeat x-pack build contains all functionality from OSS Winlogbeat. - env.BUILD_WINLOGBEAT = isChangedOSSCode(getVendorPatterns('winlogbeat')) - env.BUILD_WINLOGBEAT_XPACK = isChangedXPackCode(getVendorPatterns('x-pack/winlogbeat')) - - // Elastic-agent is a self-contained product, that depends on libbeat only. - // The agent acts as a supervisor for other Beats like Filebeat or Metricbeat. - // The agent is available as x-pack build only. - env.BUILD_ELASTIC_AGENT_XPACK = isChangedXPackCode(getVendorPatterns('x-pack/elastic-agent')) - - // The Kubernetes test use Filebeat and Metricbeat, but only need to be run - // if the deployment scripts have been updated. No Beats specific testing is - // involved. - env.BUILD_KUBERNETES = isChanged(["^deploy/kubernetes/.*"]) - - def generatorPatterns = ['^generator/.*'] - generatorPatterns.addAll(getVendorPatterns('generator/common/beatgen')) - generatorPatterns.addAll(getVendorPatterns('metricbeat/beater')) - env.BUILD_GENERATOR = isChangedOSSCode(generatorPatterns) - - // Skip all the stages for changes only related to the documentation - env.ONLY_DOCS = isDocChangedOnly() - - // Run the ITs by running only if the changeset affects a specific module. - // For such, it's required to look for changes under the module folder and exclude anything else - // such as ascidoc and png files. - env.MODULE = getGitMatchingGroup(pattern: '[a-z0-9]+beat\\/module\\/([^\\/]+)\\/.*', exclude: '^(((?!\\/module\\/).)*$|.*\\.asciidoc|.*\\.png)') -} - -/** - This method verifies if the changeset for the current pull request affect only changes related - to documentation, such as asciidoc and png files. -*/ -def isDocChangedOnly(){ - if (params.runAllStages || !env.CHANGE_ID?.trim()) { - log(level: 'INFO', text: 'Speed build for docs only is disabled for branches/tags or when forcing with the runAllStages parameter.') - return 'false' - } else { - log(level: "INFO", text: 'Check if the speed build for docs is enabled.') - return isGitRegionMatch(patterns: ['.*\\.(asciidoc|png)'], shouldMatchAll: true) - } -} - -/** - This method grab the dependencies of a Go module and transform them on regexp -*/ -def getVendorPatterns(beatName){ - def os = goos() - def goRoot = "${env.WORKSPACE}/.gvm/versions/go${GO_VERSION}.${os}.amd64" - def output = "" - - withEnv([ - "HOME=${env.WORKSPACE}/${env.BASE_DIR}", - "PATH=${env.WORKSPACE}/bin:${goRoot}/bin:${env.PATH}", - ]) { - output = sh(label: 'Get vendor dependency patterns', returnStdout: true, script: """ - go list -deps ./${beatName} \ - | grep 'elastic/beats' \ - | sed -e "s#github.com/elastic/beats/v7/##g" \ - | awk '{print "^" \$1 "/.*"}' - """) - } - return output?.split('\n').collect{ item -> item as String } -} - -def setGitConfig(){ - sh(label: 'check git config', script: ''' - if [ -z "$(git config --get user.email)" ]; then - git config user.email "beatsmachine@users.noreply.github.com" - git config user.name "beatsmachine" - fi - ''') -} - -def isDockerInstalled(){ - return sh(label: 'check for Docker', script: 'command -v docker', returnStatus: true) -} - -def junitAndStore(Map params = [:]){ - junit(params) - // STAGE_NAME env variable could be null in some cases, so let's use the currentmilliseconds - def stageName = env.STAGE_NAME ? env.STAGE_NAME.replaceAll("[\\W]|_",'-') : "uncategorized-${new java.util.Date().getTime()}" - stash(includes: params.testResults, allowEmpty: true, name: stageName, useDefaultExcludes: true) - stashedTestReports[stageName] = stageName -} - -def runbld() { - catchError(buildResult: 'SUCCESS', message: 'runbld post build action failed.') { - if (stashedTestReports) { - dir("${env.BASE_DIR}") { - sh(label: 'Prepare workspace context', - script: 'find . -type f -name "TEST*.xml" -path "*/build/*" -delete') - // Unstash the test reports - stashedTestReports.each { k, v -> - dir(k) { - unstash(v) - } - } - sh(label: 'Process JUnit reports with runbld', - script: '''\ - cat >./runbld-script < Date: Thu, 8 Oct 2020 15:27:14 +0200 Subject: [PATCH 62/93] Fix function that parses from/to/contact headers (#21672) --- packetbeat/protos/sip/plugin.go | 35 +++++++++++++++++++++------- packetbeat/protos/sip/plugin_test.go | 12 +++++++--- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/packetbeat/protos/sip/plugin.go b/packetbeat/protos/sip/plugin.go index e4cc0364d9a..14b56aeda45 100644 --- a/packetbeat/protos/sip/plugin.go +++ b/packetbeat/protos/sip/plugin.go @@ -499,27 +499,46 @@ func populateBodyFields(m *message, pbf *pb.Fields, fields *ProtocolFields) { func parseFromToContact(fromTo common.NetString) (displayInfo, uri common.NetString, params map[string]common.NetString) { params = make(map[string]common.NetString) - pos := bytes.IndexByte(fromTo, '<') - if pos == -1 { - pos = bytes.IndexByte(fromTo, ' ') - } + fromTo = bytes.TrimSpace(fromTo) + + var uriIsWrapped bool + pos := func() int { + // look for the beginning of a url wrapped in <...> + if pos := bytes.IndexByte(fromTo, '<'); pos > -1 { + uriIsWrapped = true + return pos + } + // if there is no < char, it means there is no display info, and + // that the url starts from the beginning + // https://tools.ietf.org/html/rfc3261#section-20.10 + return 0 + }() displayInfo = bytes.Trim(fromTo[:pos], "'\"\t ") endURIPos := func() int { - if fromTo[pos] == '<' { + if uriIsWrapped { return bytes.IndexByte(fromTo, '>') } return bytes.IndexByte(fromTo, ';') }() + // not wrapped and no header params if endURIPos == -1 { - uri = bytes.TrimRight(fromTo[pos:], ">") - return + uri = fromTo[pos:] + return displayInfo, uri, params } - pos += 1 + + // if wrapped, we want to get over the < char + if uriIsWrapped { + pos += 1 + } + + // if wrapped, we will get the string between <...> + // if not wrapped, we will get the value before the header params (until ;) uri = fromTo[pos:endURIPos] + // parse the header params pos = endURIPos + 1 for _, param := range bytes.Split(fromTo[pos:], []byte(";")) { kv := bytes.SplitN(param, []byte("="), 2) diff --git a/packetbeat/protos/sip/plugin_test.go b/packetbeat/protos/sip/plugin_test.go index fc9ee53aff2..d8c09f5b307 100644 --- a/packetbeat/protos/sip/plugin_test.go +++ b/packetbeat/protos/sip/plugin_test.go @@ -65,11 +65,11 @@ func TestParseFromTo(t *testing.T) { assert.Equal(t, common.NetString(nil), params["tag"]) // From - displayInfo, uri, params = parseFromToContact(common.NetString("\"PCMU/8000\" ;tag=1")) + displayInfo, uri, params = parseFromToContact(common.NetString("\"PCMU/8000\" ;tag=1")) assert.Equal(t, common.NetString("PCMU/8000"), displayInfo) assert.Equal(t, common.NetString("sip:sipp@10.0.2.15:5060"), uri) assert.Equal(t, common.NetString("1"), params["tag"]) - displayInfo, uri, params = parseFromToContact(common.NetString("\"Matthew Hodgson\" ;tag=5c7cdb68")) + displayInfo, uri, params = parseFromToContact(common.NetString(" \"Matthew Hodgson\" ;tag=5c7cdb68")) assert.Equal(t, common.NetString("Matthew Hodgson"), displayInfo) assert.Equal(t, common.NetString("sip:matthew@mxtelecom.com"), uri) assert.Equal(t, common.NetString("5c7cdb68"), params["tag"]) @@ -83,7 +83,7 @@ func TestParseFromTo(t *testing.T) { assert.Equal(t, common.NetString(nil), params["tag"]) // Contact - displayInfo, uri, _ = parseFromToContact(common.NetString("")) + displayInfo, uri, _ = parseFromToContact(common.NetString(" ")) assert.Equal(t, common.NetString(nil), displayInfo) assert.Equal(t, common.NetString("sip:test@10.0.2.15:5060;transport=udp"), uri) displayInfo, uri, params = parseFromToContact(common.NetString(";expires=1200;q=0.500")) @@ -100,6 +100,12 @@ func TestParseFromTo(t *testing.T) { assert.Equal(t, common.NetString("Mr. Watson"), displayInfo) assert.Equal(t, common.NetString("mailto:watson@bell-telephone.com"), uri) assert.Equal(t, common.NetString("0.1"), params["q"]) + + // url is not bounded by <...> + displayInfo, uri, params = parseFromToContact(common.NetString(" sip:test@10.0.2.15:5060;transport=udp")) + assert.Equal(t, common.NetString(nil), displayInfo) + assert.Equal(t, common.NetString("sip:test@10.0.2.15:5060"), uri) + assert.Equal(t, common.NetString("udp"), params["transport"]) } func TestParseUDP(t *testing.T) { From 5b69349d02b3fc92605ccfe796b6dd558fa18846 Mon Sep 17 00:00:00 2001 From: Lee Hinman <57081003+leehinman@users.noreply.github.com> Date: Thu, 8 Oct 2020 09:27:33 -0500 Subject: [PATCH 63/93] [Winlogbeat] Remove brittle configuration validation from wineventlog (#21593) * Remove brittle configuration validation from wineventlog - removed config keys checking - update unit tests Closes #21220 --- winlogbeat/eventlog/eventlogging.go | 5 +--- winlogbeat/eventlog/factory.go | 28 +++---------------- winlogbeat/eventlog/wineventlog.go | 6 +--- .../eventlog/wineventlog_expirimental.go | 2 +- winlogbeat/tests/system/test_eventlogging.py | 19 ------------- winlogbeat/tests/system/test_wineventlog.py | 17 ----------- 6 files changed, 7 insertions(+), 70 deletions(-) diff --git a/winlogbeat/eventlog/eventlogging.go b/winlogbeat/eventlog/eventlogging.go index 963797264b2..01465d933fc 100644 --- a/winlogbeat/eventlog/eventlogging.go +++ b/winlogbeat/eventlog/eventlogging.go @@ -40,9 +40,6 @@ const ( eventLoggingAPIName = "eventlogging" ) -var eventLoggingConfigKeys = common.MakeStringSet(append(commonConfigKeys, - "ignore_older", "read_buffer_size", "format_buffer_size")...) - type eventLoggingConfig struct { ConfigCommon `config:",inline"` IgnoreOlder time.Duration `config:"ignore_older"` @@ -284,7 +281,7 @@ func newEventLogging(options *common.Config) (EventLog, error) { ReadBufferSize: win.MaxEventBufferSize, FormatBufferSize: win.MaxFormatMessageBufferSize, } - if err := readConfig(options, &c, eventLoggingConfigKeys); err != nil { + if err := readConfig(options, &c); err != nil { return nil, err } diff --git a/winlogbeat/eventlog/factory.go b/winlogbeat/eventlog/factory.go index f66c158b2f2..9ee8e0d144f 100644 --- a/winlogbeat/eventlog/factory.go +++ b/winlogbeat/eventlog/factory.go @@ -22,14 +22,9 @@ import ( "sort" "strings" - "github.com/joeshaw/multierror" - "github.com/elastic/beats/v7/libbeat/common" ) -var commonConfigKeys = []string{"type", "api", "name", "fields", "fields_under_root", - "tags", "processors", "index", "id", "meta", "revision"} - // ConfigCommon is the common configuration data used to instantiate a new // EventLog. Each implementation is free to support additional configuration // options. @@ -42,33 +37,18 @@ type validator interface { Validate() error } -func readConfig( - c *common.Config, - config interface{}, - validKeys common.StringSet, -) error { +func readConfig(c *common.Config, config interface{}) error { if err := c.Unpack(config); err != nil { return fmt.Errorf("failed unpacking config. %v", err) } - var errs multierror.Errors - if len(validKeys) > 0 { - // Check for invalid keys. - for _, k := range c.GetFields() { - if !validKeys.Has(k) { - errs = append(errs, fmt.Errorf("invalid event log key '%s' "+ - "found. Valid keys are %s", k, strings.Join(validKeys.ToSlice(), ", "))) - } - } - } - if v, ok := config.(validator); ok { if err := v.Validate(); err != nil { - errs = append(errs, err) + return err } } - return errs.Err() + return nil } // Producer produces a new event log instance for reading event log records. @@ -114,7 +94,7 @@ func New(options *common.Config) (EventLog, error) { } var config ConfigCommon - if err := readConfig(options, &config, nil); err != nil { + if err := readConfig(options, &config); err != nil { return nil, err } diff --git a/winlogbeat/eventlog/wineventlog.go b/winlogbeat/eventlog/wineventlog.go index db00f239974..7ee6c62cf18 100644 --- a/winlogbeat/eventlog/wineventlog.go +++ b/winlogbeat/eventlog/wineventlog.go @@ -47,10 +47,6 @@ const ( winEventLogAPIName = "wineventlog" ) -var winEventLogConfigKeys = common.MakeStringSet(append(commonConfigKeys, - "batch_read_size", "ignore_older", "include_xml", "event_id", "forwarded", - "level", "provider", "no_more_events")...) - type winEventLogConfig struct { ConfigCommon `config:",inline"` BatchReadSize int `config:"batch_read_size"` // Maximum number of events that Read will return. @@ -366,7 +362,7 @@ func (l *winEventLog) buildRecordFromXML(x []byte, recoveredErr error) (Record, // using the Windows Event Log. func newWinEventLog(options *common.Config) (EventLog, error) { c := defaultWinEventLogConfig - if err := readConfig(options, &c, winEventLogConfigKeys); err != nil { + if err := readConfig(options, &c); err != nil { return nil, err } diff --git a/winlogbeat/eventlog/wineventlog_expirimental.go b/winlogbeat/eventlog/wineventlog_expirimental.go index 5952aad0f5a..301b2cf4f0b 100644 --- a/winlogbeat/eventlog/wineventlog_expirimental.go +++ b/winlogbeat/eventlog/wineventlog_expirimental.go @@ -243,7 +243,7 @@ func newWinEventLogExp(options *common.Config) (EventLog, error) { cfgwarn.Experimental("The %s event log reader is experimental.", winEventLogExpAPIName) c := winEventLogConfig{BatchReadSize: 512} - if err := readConfig(options, &c, winEventLogConfigKeys); err != nil { + if err := readConfig(options, &c); err != nil { return nil, err } diff --git a/winlogbeat/tests/system/test_eventlogging.py b/winlogbeat/tests/system/test_eventlogging.py index dd763104fa5..0d486656054 100644 --- a/winlogbeat/tests/system/test_eventlogging.py +++ b/winlogbeat/tests/system/test_eventlogging.py @@ -156,25 +156,6 @@ def test_ignore_older(self): self.assertEqual(evts[0]["winlog.event_id"], 10) self.assertEqual(evts[0]["event.code"], 10) - def test_unknown_eventlog_config(self): - """ - eventlogging - Unknown config parameter - """ - self.render_config_template( - event_logs=[ - { - "name": self.providerName, - "api": self.api, - "event_id": "10, 12", - "level": "info", - "provider": ["me"], - "include_xml": True, - } - ] - ) - self.start_beat().check_wait(exit_code=1) - assert self.log_contains("4 errors: invalid event log key") - def test_utf16_characters(self): """ eventlogging - UTF-16 characters diff --git a/winlogbeat/tests/system/test_wineventlog.py b/winlogbeat/tests/system/test_wineventlog.py index ea390475279..363e90edbd2 100644 --- a/winlogbeat/tests/system/test_wineventlog.py +++ b/winlogbeat/tests/system/test_wineventlog.py @@ -311,23 +311,6 @@ def test_query_multi_param(self): self.assertTrue(len(evts), 1) self.assertEqual(evts[0]["message"], "selected") - def test_unknown_eventlog_config(self): - """ - wineventlog - Unknown config parameter - """ - self.render_config_template( - event_logs=[ - { - "name": self.providerName, - "api": self.api, - "forwarded": False, - "invalid": "garbage"} - ] - ) - self.start_beat().check_wait(exit_code=1) - assert self.log_contains( - "1 error: invalid event log key 'invalid' found.") - def test_utf16_characters(self): """ wineventlog - UTF-16 characters From e4df4501bdbf9edb89421746f559a89688fff71e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 8 Oct 2020 19:46:45 +0200 Subject: [PATCH 64/93] fix: remove extra curly brace in script (#21692) * fix: remove extra curly brace * chore: proper indent --- .ci/packaging.groovy | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.ci/packaging.groovy b/.ci/packaging.groovy index 81ff5b1994a..c65fd7f8b56 100644 --- a/.ci/packaging.groovy +++ b/.ci/packaging.groovy @@ -215,12 +215,12 @@ def tagAndPush(name){ docker push ${newName} docker tag ${oldName} ${commitName} docker push ${commitName} - """, returnStatus: true) - if ( status > 0 && iterations < 3) { - error('tag and push failed, retry') - } else if ( status > 0 ) { - log(level: 'WARN', text: "${name} doesn't have ${variant} docker images. See https://github.com/elastic/beats/pull/21621") - } + """, returnStatus: true) + + if ( status > 0 && iterations < 3) { + error('tag and push failed, retry') + } else if ( status > 0 ) { + log(level: 'WARN', text: "${name} doesn't have ${variant} docker images. See https://github.com/elastic/beats/pull/21621") } } } From 1abe97b20d708d9dd61018163a7d45616b415c59 Mon Sep 17 00:00:00 2001 From: Adrian Serrano Date: Fri, 9 Oct 2020 09:33:35 +0200 Subject: [PATCH 65/93] Make o365audit input cancellable (#21647) PR #21258 introduced a restart mechanism for o365input so that it didn't stop working once a fatal error was found. This updates the restart delay to use a cancellation-context-aware method so that the input doesn't block Filebeat termination. --- x-pack/filebeat/input/o365audit/input.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/x-pack/filebeat/input/o365audit/input.go b/x-pack/filebeat/input/o365audit/input.go index 67013dc6a71..2cf76de9ee3 100644 --- a/x-pack/filebeat/input/o365audit/input.go +++ b/x-pack/filebeat/input/o365audit/input.go @@ -21,14 +21,12 @@ import ( "github.com/elastic/beats/v7/libbeat/logp" "github.com/elastic/beats/v7/x-pack/filebeat/input/o365audit/poll" "github.com/elastic/go-concert/ctxtool" + "github.com/elastic/go-concert/timed" ) const ( pluginName = "o365audit" fieldsPrefix = pluginName - - // How long to retry when a fatal error is encountered in the input. - failureRetryInterval = time.Minute * 5 ) type o365input struct { @@ -126,8 +124,8 @@ func (inp *o365input) Run( } publisher.Publish(event, nil) ctx.Logger.Errorf("Input failed: %v", err) - ctx.Logger.Infof("Restarting in %v", failureRetryInterval) - time.Sleep(failureRetryInterval) + ctx.Logger.Infof("Restarting in %v", inp.config.API.ErrorRetryInterval) + timed.Wait(ctx.Cancelation, inp.config.API.ErrorRetryInterval) } } return nil From 3aceb31568b531063369c191636a36b68f48ae59 Mon Sep 17 00:00:00 2001 From: Andrew Wilkins Date: Mon, 12 Oct 2020 12:34:58 +0800 Subject: [PATCH 66/93] libbeat/logp: introduce Logger.WithOptions (#21671) * libbeat/logp: introduce Logger.WithOptions Add a Logger.WithOptions method, which clones the logger and applies given options. For example, this can be used to obtain a clone of the logger with sampling/rate limiting applied. --- libbeat/logp/logger.go | 6 +++++ libbeat/logp/logger_test.go | 52 +++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 libbeat/logp/logger_test.go diff --git a/libbeat/logp/logger.go b/libbeat/logp/logger.go index 6f1c42fe022..2bbe6b3ce53 100644 --- a/libbeat/logp/logger.go +++ b/libbeat/logp/logger.go @@ -50,6 +50,12 @@ func NewLogger(selector string, options ...LogOption) *Logger { return newLogger(loadLogger().rootLogger, selector, options...) } +// WithOptions returns a clone of l with options applied. +func (l *Logger) WithOptions(options ...LogOption) *Logger { + cloned := l.logger.WithOptions(options...) + return &Logger{cloned, cloned.Sugar()} +} + // With creates a child logger and adds structured context to it. Fields added // to the child don't affect the parent, and vice versa. func (l *Logger) With(args ...interface{}) *Logger { diff --git a/libbeat/logp/logger_test.go b/libbeat/logp/logger_test.go new file mode 100644 index 00000000000..eaf8a1070ce --- /dev/null +++ b/libbeat/logp/logger_test.go @@ -0,0 +1,52 @@ +// 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 logp + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest/observer" +) + +func TestLoggerWithOptions(t *testing.T) { + core1, observed1 := observer.New(zapcore.DebugLevel) + core2, observed2 := observer.New(zapcore.DebugLevel) + + logger1 := NewLogger("bo", zap.WrapCore(func(in zapcore.Core) zapcore.Core { + return zapcore.NewTee(in, core1) + })) + logger2 := logger1.WithOptions(zap.WrapCore(func(in zapcore.Core) zapcore.Core { + return zapcore.NewTee(in, core2) + })) + + logger1.Info("hello logger1") // should just go to the first observer + logger2.Info("hello logger1 and logger2") // should go to both observers + + observedEntries1 := observed1.All() + require.Len(t, observedEntries1, 2) + assert.Equal(t, "hello logger1", observedEntries1[0].Message) + assert.Equal(t, "hello logger1 and logger2", observedEntries1[1].Message) + + observedEntries2 := observed2.All() + require.Len(t, observedEntries2, 1) + assert.Equal(t, "hello logger1 and logger2", observedEntries2[0].Message) +} From d35dfb53111a86ed0e484f8450ffcf1474be60e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9mi=20V=C3=A1nyi?= Date: Mon, 12 Oct 2020 10:53:59 +0200 Subject: [PATCH 67/93] Add configuration of filestream input (#21565) --- .../config/filebeat.inputs.reference.yml.tmpl | 140 ++++++++++++++++++ .../_meta/config/filebeat.inputs.yml.tmpl | 29 ++++ filebeat/filebeat.reference.yml | 140 ++++++++++++++++++ filebeat/filebeat.yml | 29 ++++ filebeat/input/filestream/config.go | 18 +-- filebeat/input/filestream/input.go | 16 +- x-pack/filebeat/filebeat.reference.yml | 140 ++++++++++++++++++ x-pack/filebeat/filebeat.yml | 29 ++++ 8 files changed, 524 insertions(+), 17 deletions(-) diff --git a/filebeat/_meta/config/filebeat.inputs.reference.yml.tmpl b/filebeat/_meta/config/filebeat.inputs.reference.yml.tmpl index c920b7dbec8..7eceb559f16 100644 --- a/filebeat/_meta/config/filebeat.inputs.reference.yml.tmpl +++ b/filebeat/_meta/config/filebeat.inputs.reference.yml.tmpl @@ -11,6 +11,7 @@ filebeat.inputs: # # Possible options are: # * log: Reads every line of the log file (default) +# * filestream: Improved version of log input. Experimental. # * stdin: Reads the standard in #------------------------------ Log input -------------------------------- @@ -231,6 +232,145 @@ filebeat.inputs: # Defines if inputs is enabled #enabled: true +#--------------------------- Filestream input ---------------------------- +- type: filestream + + # Change to true to enable this input configuration. + enabled: false + + # Paths that should be crawled and fetched. Glob based paths. + # To fetch all ".log" files from a specific level of subdirectories + # /var/log/*/*.log can be used. + # For each file found under this path, a harvester is started. + # Make sure not file is defined twice as this can lead to unexpected behaviour. + paths: + - /var/log/*.log + #- c:\programdata\elasticsearch\logs\* + + # Configure the file encoding for reading files with international characters + # following the W3C recommendation for HTML5 (http://www.w3.org/TR/encoding). + # Some sample encodings: + # plain, utf-8, utf-16be-bom, utf-16be, utf-16le, big5, gb18030, gbk, + # hz-gb-2312, euc-kr, euc-jp, iso-2022-jp, shift-jis, ... + #encoding: plain + + + # Exclude lines. A list of regular expressions to match. It drops the lines that are + # matching any regular expression from the list. The include_lines is called before + # exclude_lines. By default, no lines are dropped. + #exclude_lines: ['^DBG'] + + # Include lines. A list of regular expressions to match. It exports the lines that are + # matching any regular expression from the list. The include_lines is called before + # exclude_lines. By default, all the lines are exported. + #include_lines: ['^ERR', '^WARN'] + + ### Prospector options + + # How often the input checks for new files in the paths that are specified + # for harvesting. Specify 1s to scan the directory as frequently as possible + # without causing Filebeat to scan too frequently. Default: 10s. + #prospector.scanner.check_interval: 10s + + # Exclude files. A list of regular expressions to match. Filebeat drops the files that + # are matching any regular expression from the list. By default, no files are dropped. + #prospector.scanner.exclude_files: ['.gz$'] + + # Expand "**" patterns into regular glob patterns. + #prospector.scanner.recursive_glob: true + + # If symlinks is enabled, symlinks are opened and harvested. The harvester is opening the + # original for harvesting but will report the symlink name as source. + #prospector.scanner.symlinks: false + + ### State options + + # Files for the modification data is older then clean_inactive the state from the registry is removed + # By default this is disabled. + #clean_inactive: 0 + + # Removes the state for file which cannot be found on disk anymore immediately + #clean_removed: true + + # Method to determine if two files are the same or not. By default + # the Beat considers two files the same if their inode and device id are the same. + #file_identity.native: ~ + + # Optional additional fields. These fields can be freely picked + # to add additional information to the crawled log files for filtering + #fields: + # level: debug + # review: 1 + + # Set to true to publish fields with null values in events. + #keep_null: false + + # By default, all events contain `host.name`. This option can be set to true + # to disable the addition of this field to all events. The default value is + # false. + #publisher_pipeline.disable_host: false + + # Ignore files which were modified more then the defined timespan in the past. + # ignore_older is disabled by default, so no files are ignored by setting it to 0. + # Time strings like 2h (2 hours), 5m (5 minutes) can be used. + #ignore_older: 0 + + # Defines the buffer size every harvester uses when fetching the file + #harvester_buffer_size: 16384 + + # Maximum number of bytes a single log event can have + # All bytes after max_bytes are discarded and not sent. The default is 10MB. + # This is especially useful for multiline log messages which can get large. + #message_max_bytes: 10485760 + + # Characters which separate the lines. Valid values: auto, line_feed, vertical_tab, form_feed, + # carriage_return, carriage_return_line_feed, next_line, line_separator, paragraph_separator. + #line_terminator: auto + + # The Ingest Node pipeline ID associated with this input. If this is set, it + # overwrites the pipeline option from the Elasticsearch output. + #pipeline: + + # Backoff values define how aggressively filebeat crawls new files for updates + # The default values can be used in most cases. Backoff defines how long it is waited + # to check a file again after EOF is reached. Default is 1s which means the file + # is checked every second if new lines were added. This leads to a near real time crawling. + # Every time a new line appears, backoff is reset to the initial value. + #backoff.init: 1s + + # Max backoff defines what the maximum backoff time is. After having backed off multiple times + # from checking the files, the waiting time will never exceed max_backoff independent of the + # backoff factor. Having it set to 10s means in the worst case a new line can be added to a log + # file after having backed off multiple times, it takes a maximum of 10s to read the new line + #backoff.max: 10s + + ### Harvester closing options + + # Close inactive closes the file handler after the predefined period. + # The period starts when the last line of the file was, not the file ModTime. + # Time strings like 2h (2 hours), 5m (5 minutes) can be used. + #close.on_state_change.inactive: 5m + + # Close renamed closes a file handler when the file is renamed or rotated. + # Note: Potential data loss. Make sure to read and understand the docs for this option. + #close.on_state_change.renamed: false + + # When enabling this option, a file handler is closed immediately in case a file can't be found + # any more. In case the file shows up again later, harvesting will continue at the last known position + # after scan_frequency. + #close.on_state_change.removed: true + + # Closes the file handler as soon as the harvesters reaches the end of the file. + # By default this option is disabled. + # Note: Potential data loss. Make sure to read and understand the docs for this option. + #close.reader.eof: false + + # Close timeout closes the harvester after the predefined time. + # This is independent if the harvester did finish reading the file or not. + # By default this option is disabled. + # Note: Potential data loss. Make sure to read and understand the docs for this option. + #close.reader.after_interval: 0 + #----------------------------- Stdin input ------------------------------- # Configuration to use stdin input #- type: stdin diff --git a/filebeat/_meta/config/filebeat.inputs.yml.tmpl b/filebeat/_meta/config/filebeat.inputs.yml.tmpl index a7bd1b5eaa6..70d52cbb9c6 100644 --- a/filebeat/_meta/config/filebeat.inputs.yml.tmpl +++ b/filebeat/_meta/config/filebeat.inputs.yml.tmpl @@ -49,3 +49,32 @@ filebeat.inputs: # that was (not) matched before or after or as long as a pattern is not matched based on negate. # Note: After is the equivalent to previous and before is the equivalent to to next in Logstash #multiline.match: after + +# filestream is an experimental input. It is going to replace log input in the future. +- type: filestream + + # Change to true to enable this input configuration. + enabled: false + + # Paths that should be crawled and fetched. Glob based paths. + paths: + - /var/log/*.log + #- c:\programdata\elasticsearch\logs\* + + # Exclude lines. A list of regular expressions to match. It drops the lines that are + # matching any regular expression from the list. + #exclude_lines: ['^DBG'] + + # Include lines. A list of regular expressions to match. It exports the lines that are + # matching any regular expression from the list. + #include_lines: ['^ERR', '^WARN'] + + # Exclude files. A list of regular expressions to match. Filebeat drops the files that + # are matching any regular expression from the list. By default, no files are dropped. + #prospector.scanner.exclude_files: ['.gz$'] + + # Optional additional fields. These fields can be freely picked + # to add additional information to the crawled log files for filtering + #fields: + # level: debug + # review: 1 diff --git a/filebeat/filebeat.reference.yml b/filebeat/filebeat.reference.yml index bf29e0715ed..3a0d1d5d19c 100644 --- a/filebeat/filebeat.reference.yml +++ b/filebeat/filebeat.reference.yml @@ -398,6 +398,7 @@ filebeat.inputs: # # Possible options are: # * log: Reads every line of the log file (default) +# * filestream: Improved version of log input. Experimental. # * stdin: Reads the standard in #------------------------------ Log input -------------------------------- @@ -618,6 +619,145 @@ filebeat.inputs: # Defines if inputs is enabled #enabled: true +#--------------------------- Filestream input ---------------------------- +- type: filestream + + # Change to true to enable this input configuration. + enabled: false + + # Paths that should be crawled and fetched. Glob based paths. + # To fetch all ".log" files from a specific level of subdirectories + # /var/log/*/*.log can be used. + # For each file found under this path, a harvester is started. + # Make sure not file is defined twice as this can lead to unexpected behaviour. + paths: + - /var/log/*.log + #- c:\programdata\elasticsearch\logs\* + + # Configure the file encoding for reading files with international characters + # following the W3C recommendation for HTML5 (http://www.w3.org/TR/encoding). + # Some sample encodings: + # plain, utf-8, utf-16be-bom, utf-16be, utf-16le, big5, gb18030, gbk, + # hz-gb-2312, euc-kr, euc-jp, iso-2022-jp, shift-jis, ... + #encoding: plain + + + # Exclude lines. A list of regular expressions to match. It drops the lines that are + # matching any regular expression from the list. The include_lines is called before + # exclude_lines. By default, no lines are dropped. + #exclude_lines: ['^DBG'] + + # Include lines. A list of regular expressions to match. It exports the lines that are + # matching any regular expression from the list. The include_lines is called before + # exclude_lines. By default, all the lines are exported. + #include_lines: ['^ERR', '^WARN'] + + ### Prospector options + + # How often the input checks for new files in the paths that are specified + # for harvesting. Specify 1s to scan the directory as frequently as possible + # without causing Filebeat to scan too frequently. Default: 10s. + #prospector.scanner.check_interval: 10s + + # Exclude files. A list of regular expressions to match. Filebeat drops the files that + # are matching any regular expression from the list. By default, no files are dropped. + #prospector.scanner.exclude_files: ['.gz$'] + + # Expand "**" patterns into regular glob patterns. + #prospector.scanner.recursive_glob: true + + # If symlinks is enabled, symlinks are opened and harvested. The harvester is opening the + # original for harvesting but will report the symlink name as source. + #prospector.scanner.symlinks: false + + ### State options + + # Files for the modification data is older then clean_inactive the state from the registry is removed + # By default this is disabled. + #clean_inactive: 0 + + # Removes the state for file which cannot be found on disk anymore immediately + #clean_removed: true + + # Method to determine if two files are the same or not. By default + # the Beat considers two files the same if their inode and device id are the same. + #file_identity.native: ~ + + # Optional additional fields. These fields can be freely picked + # to add additional information to the crawled log files for filtering + #fields: + # level: debug + # review: 1 + + # Set to true to publish fields with null values in events. + #keep_null: false + + # By default, all events contain `host.name`. This option can be set to true + # to disable the addition of this field to all events. The default value is + # false. + #publisher_pipeline.disable_host: false + + # Ignore files which were modified more then the defined timespan in the past. + # ignore_older is disabled by default, so no files are ignored by setting it to 0. + # Time strings like 2h (2 hours), 5m (5 minutes) can be used. + #ignore_older: 0 + + # Defines the buffer size every harvester uses when fetching the file + #harvester_buffer_size: 16384 + + # Maximum number of bytes a single log event can have + # All bytes after max_bytes are discarded and not sent. The default is 10MB. + # This is especially useful for multiline log messages which can get large. + #message_max_bytes: 10485760 + + # Characters which separate the lines. Valid values: auto, line_feed, vertical_tab, form_feed, + # carriage_return, carriage_return_line_feed, next_line, line_separator, paragraph_separator. + #line_terminator: auto + + # The Ingest Node pipeline ID associated with this input. If this is set, it + # overwrites the pipeline option from the Elasticsearch output. + #pipeline: + + # Backoff values define how aggressively filebeat crawls new files for updates + # The default values can be used in most cases. Backoff defines how long it is waited + # to check a file again after EOF is reached. Default is 1s which means the file + # is checked every second if new lines were added. This leads to a near real time crawling. + # Every time a new line appears, backoff is reset to the initial value. + #backoff.init: 1s + + # Max backoff defines what the maximum backoff time is. After having backed off multiple times + # from checking the files, the waiting time will never exceed max_backoff independent of the + # backoff factor. Having it set to 10s means in the worst case a new line can be added to a log + # file after having backed off multiple times, it takes a maximum of 10s to read the new line + #backoff.max: 10s + + ### Harvester closing options + + # Close inactive closes the file handler after the predefined period. + # The period starts when the last line of the file was, not the file ModTime. + # Time strings like 2h (2 hours), 5m (5 minutes) can be used. + #close.on_state_change.inactive: 5m + + # Close renamed closes a file handler when the file is renamed or rotated. + # Note: Potential data loss. Make sure to read and understand the docs for this option. + #close.on_state_change.renamed: false + + # When enabling this option, a file handler is closed immediately in case a file can't be found + # any more. In case the file shows up again later, harvesting will continue at the last known position + # after scan_frequency. + #close.on_state_change.removed: true + + # Closes the file handler as soon as the harvesters reaches the end of the file. + # By default this option is disabled. + # Note: Potential data loss. Make sure to read and understand the docs for this option. + #close.reader.eof: false + + # Close timeout closes the harvester after the predefined time. + # This is independent if the harvester did finish reading the file or not. + # By default this option is disabled. + # Note: Potential data loss. Make sure to read and understand the docs for this option. + #close.reader.after_interval: 0 + #----------------------------- Stdin input ------------------------------- # Configuration to use stdin input #- type: stdin diff --git a/filebeat/filebeat.yml b/filebeat/filebeat.yml index 2a3e678c542..9a29bc76962 100644 --- a/filebeat/filebeat.yml +++ b/filebeat/filebeat.yml @@ -62,6 +62,35 @@ filebeat.inputs: # Note: After is the equivalent to previous and before is the equivalent to to next in Logstash #multiline.match: after +# filestream is an experimental input. It is going to replace log input in the future. +- type: filestream + + # Change to true to enable this input configuration. + enabled: false + + # Paths that should be crawled and fetched. Glob based paths. + paths: + - /var/log/*.log + #- c:\programdata\elasticsearch\logs\* + + # Exclude lines. A list of regular expressions to match. It drops the lines that are + # matching any regular expression from the list. + #exclude_lines: ['^DBG'] + + # Include lines. A list of regular expressions to match. It exports the lines that are + # matching any regular expression from the list. + #include_lines: ['^ERR', '^WARN'] + + # Exclude files. A list of regular expressions to match. Filebeat drops the files that + # are matching any regular expression from the list. By default, no files are dropped. + #prospector.scanner.exclude_files: ['.gz$'] + + # Optional additional fields. These fields can be freely picked + # to add additional information to the crawled log files for filtering + #fields: + # level: debug + # review: 1 + # ============================== Filebeat modules ============================== filebeat.config.modules: diff --git a/filebeat/input/filestream/config.go b/filebeat/input/filestream/config.go index 3ec076196f0..c2b1e838ee5 100644 --- a/filebeat/input/filestream/config.go +++ b/filebeat/input/filestream/config.go @@ -30,10 +30,11 @@ import ( // Config stores the options of a file stream. type config struct { + readerConfig + Paths []string `config:"paths"` Close closerConfig `config:"close"` FileWatcher *common.ConfigNamespace `config:"file_watcher"` - Reader readerConfig `config:"readers"` FileIdentity *common.ConfigNamespace `config:"file_identity"` CleanInactive time.Duration `config:"clean_inactive" validate:"min=0"` CleanRemoved bool `config:"clean_removed"` @@ -47,18 +48,17 @@ type closerConfig struct { } type readerCloserConfig struct { - AfterInterval time.Duration - OnEOF bool + AfterInterval time.Duration `config:"after_interval"` + OnEOF bool `config:"on_eof"` } type stateChangeCloserConfig struct { - CheckInterval time.Duration - Inactive time.Duration - Removed bool - Renamed bool + CheckInterval time.Duration `config:"check_interval" validate:"nonzero"` + Inactive time.Duration `config:"inactive"` + Removed bool `config:"removed"` + Renamed bool `config:"renamed"` } -// TODO should this be inline? type readerConfig struct { Backoff backoffConfig `config:"backoff"` BufferSize int `config:"buffer_size"` @@ -79,9 +79,9 @@ type backoffConfig struct { func defaultConfig() config { return config{ + readerConfig: defaultReaderConfig(), Paths: []string{}, Close: defaultCloserConfig(), - Reader: defaultReaderConfig(), CleanInactive: 0, CleanRemoved: true, HarvesterLimit: 0, diff --git a/filebeat/input/filestream/input.go b/filebeat/input/filestream/input.go index b6c6598c50b..9f715d1183e 100644 --- a/filebeat/input/filestream/input.go +++ b/filebeat/input/filestream/input.go @@ -94,19 +94,19 @@ func configure(cfg *common.Config) (loginp.Prospector, loginp.Harvester, error) return nil, nil, err } - encodingFactory, ok := encoding.FindEncoding(config.Reader.Encoding) + encodingFactory, ok := encoding.FindEncoding(config.Encoding) if !ok || encodingFactory == nil { - return nil, nil, fmt.Errorf("unknown encoding('%v')", config.Reader.Encoding) + return nil, nil, fmt.Errorf("unknown encoding('%v')", config.Encoding) } return prospector, &filestream{ - readerConfig: config.Reader, - bufferSize: config.Reader.BufferSize, + readerConfig: config.readerConfig, + bufferSize: config.BufferSize, encodingFactory: encodingFactory, - lineTerminator: config.Reader.LineTerminator, - excludeLines: config.Reader.ExcludeLines, - includeLines: config.Reader.IncludeLines, - maxBytes: config.Reader.MaxBytes, + lineTerminator: config.LineTerminator, + excludeLines: config.ExcludeLines, + includeLines: config.IncludeLines, + maxBytes: config.MaxBytes, closerConfig: config.Close, }, nil } diff --git a/x-pack/filebeat/filebeat.reference.yml b/x-pack/filebeat/filebeat.reference.yml index 2ffff82135e..49ede1c7d24 100644 --- a/x-pack/filebeat/filebeat.reference.yml +++ b/x-pack/filebeat/filebeat.reference.yml @@ -1783,6 +1783,7 @@ filebeat.inputs: # # Possible options are: # * log: Reads every line of the log file (default) +# * filestream: Improved version of log input. Experimental. # * stdin: Reads the standard in #------------------------------ Log input -------------------------------- @@ -2003,6 +2004,145 @@ filebeat.inputs: # Defines if inputs is enabled #enabled: true +#--------------------------- Filestream input ---------------------------- +- type: filestream + + # Change to true to enable this input configuration. + enabled: false + + # Paths that should be crawled and fetched. Glob based paths. + # To fetch all ".log" files from a specific level of subdirectories + # /var/log/*/*.log can be used. + # For each file found under this path, a harvester is started. + # Make sure not file is defined twice as this can lead to unexpected behaviour. + paths: + - /var/log/*.log + #- c:\programdata\elasticsearch\logs\* + + # Configure the file encoding for reading files with international characters + # following the W3C recommendation for HTML5 (http://www.w3.org/TR/encoding). + # Some sample encodings: + # plain, utf-8, utf-16be-bom, utf-16be, utf-16le, big5, gb18030, gbk, + # hz-gb-2312, euc-kr, euc-jp, iso-2022-jp, shift-jis, ... + #encoding: plain + + + # Exclude lines. A list of regular expressions to match. It drops the lines that are + # matching any regular expression from the list. The include_lines is called before + # exclude_lines. By default, no lines are dropped. + #exclude_lines: ['^DBG'] + + # Include lines. A list of regular expressions to match. It exports the lines that are + # matching any regular expression from the list. The include_lines is called before + # exclude_lines. By default, all the lines are exported. + #include_lines: ['^ERR', '^WARN'] + + ### Prospector options + + # How often the input checks for new files in the paths that are specified + # for harvesting. Specify 1s to scan the directory as frequently as possible + # without causing Filebeat to scan too frequently. Default: 10s. + #prospector.scanner.check_interval: 10s + + # Exclude files. A list of regular expressions to match. Filebeat drops the files that + # are matching any regular expression from the list. By default, no files are dropped. + #prospector.scanner.exclude_files: ['.gz$'] + + # Expand "**" patterns into regular glob patterns. + #prospector.scanner.recursive_glob: true + + # If symlinks is enabled, symlinks are opened and harvested. The harvester is opening the + # original for harvesting but will report the symlink name as source. + #prospector.scanner.symlinks: false + + ### State options + + # Files for the modification data is older then clean_inactive the state from the registry is removed + # By default this is disabled. + #clean_inactive: 0 + + # Removes the state for file which cannot be found on disk anymore immediately + #clean_removed: true + + # Method to determine if two files are the same or not. By default + # the Beat considers two files the same if their inode and device id are the same. + #file_identity.native: ~ + + # Optional additional fields. These fields can be freely picked + # to add additional information to the crawled log files for filtering + #fields: + # level: debug + # review: 1 + + # Set to true to publish fields with null values in events. + #keep_null: false + + # By default, all events contain `host.name`. This option can be set to true + # to disable the addition of this field to all events. The default value is + # false. + #publisher_pipeline.disable_host: false + + # Ignore files which were modified more then the defined timespan in the past. + # ignore_older is disabled by default, so no files are ignored by setting it to 0. + # Time strings like 2h (2 hours), 5m (5 minutes) can be used. + #ignore_older: 0 + + # Defines the buffer size every harvester uses when fetching the file + #harvester_buffer_size: 16384 + + # Maximum number of bytes a single log event can have + # All bytes after max_bytes are discarded and not sent. The default is 10MB. + # This is especially useful for multiline log messages which can get large. + #message_max_bytes: 10485760 + + # Characters which separate the lines. Valid values: auto, line_feed, vertical_tab, form_feed, + # carriage_return, carriage_return_line_feed, next_line, line_separator, paragraph_separator. + #line_terminator: auto + + # The Ingest Node pipeline ID associated with this input. If this is set, it + # overwrites the pipeline option from the Elasticsearch output. + #pipeline: + + # Backoff values define how aggressively filebeat crawls new files for updates + # The default values can be used in most cases. Backoff defines how long it is waited + # to check a file again after EOF is reached. Default is 1s which means the file + # is checked every second if new lines were added. This leads to a near real time crawling. + # Every time a new line appears, backoff is reset to the initial value. + #backoff.init: 1s + + # Max backoff defines what the maximum backoff time is. After having backed off multiple times + # from checking the files, the waiting time will never exceed max_backoff independent of the + # backoff factor. Having it set to 10s means in the worst case a new line can be added to a log + # file after having backed off multiple times, it takes a maximum of 10s to read the new line + #backoff.max: 10s + + ### Harvester closing options + + # Close inactive closes the file handler after the predefined period. + # The period starts when the last line of the file was, not the file ModTime. + # Time strings like 2h (2 hours), 5m (5 minutes) can be used. + #close.on_state_change.inactive: 5m + + # Close renamed closes a file handler when the file is renamed or rotated. + # Note: Potential data loss. Make sure to read and understand the docs for this option. + #close.on_state_change.renamed: false + + # When enabling this option, a file handler is closed immediately in case a file can't be found + # any more. In case the file shows up again later, harvesting will continue at the last known position + # after scan_frequency. + #close.on_state_change.removed: true + + # Closes the file handler as soon as the harvesters reaches the end of the file. + # By default this option is disabled. + # Note: Potential data loss. Make sure to read and understand the docs for this option. + #close.reader.eof: false + + # Close timeout closes the harvester after the predefined time. + # This is independent if the harvester did finish reading the file or not. + # By default this option is disabled. + # Note: Potential data loss. Make sure to read and understand the docs for this option. + #close.reader.after_interval: 0 + #----------------------------- Stdin input ------------------------------- # Configuration to use stdin input #- type: stdin diff --git a/x-pack/filebeat/filebeat.yml b/x-pack/filebeat/filebeat.yml index 2a3e678c542..9a29bc76962 100644 --- a/x-pack/filebeat/filebeat.yml +++ b/x-pack/filebeat/filebeat.yml @@ -62,6 +62,35 @@ filebeat.inputs: # Note: After is the equivalent to previous and before is the equivalent to to next in Logstash #multiline.match: after +# filestream is an experimental input. It is going to replace log input in the future. +- type: filestream + + # Change to true to enable this input configuration. + enabled: false + + # Paths that should be crawled and fetched. Glob based paths. + paths: + - /var/log/*.log + #- c:\programdata\elasticsearch\logs\* + + # Exclude lines. A list of regular expressions to match. It drops the lines that are + # matching any regular expression from the list. + #exclude_lines: ['^DBG'] + + # Include lines. A list of regular expressions to match. It exports the lines that are + # matching any regular expression from the list. + #include_lines: ['^ERR', '^WARN'] + + # Exclude files. A list of regular expressions to match. Filebeat drops the files that + # are matching any regular expression from the list. By default, no files are dropped. + #prospector.scanner.exclude_files: ['.gz$'] + + # Optional additional fields. These fields can be freely picked + # to add additional information to the crawled log files for filtering + #fields: + # level: debug + # review: 1 + # ============================== Filebeat modules ============================== filebeat.config.modules: From f3d18f91c1aeb4d49d2214f519430e02a173b357 Mon Sep 17 00:00:00 2001 From: Blake Rouse Date: Mon, 12 Oct 2020 10:05:51 -0400 Subject: [PATCH 68/93] [Elastic Agent] Fix issue where inputs without processors defined would panic (#21628) * Fix #21581. * Add changelog entry. * Add test for processors as a map. --- x-pack/elastic-agent/CHANGELOG.next.asciidoc | 1 + .../pkg/agent/application/emitter.go | 11 +- .../pkg/agent/application/emitter_test.go | 175 ++++++++++++++++++ 3 files changed, 183 insertions(+), 4 deletions(-) diff --git a/x-pack/elastic-agent/CHANGELOG.next.asciidoc b/x-pack/elastic-agent/CHANGELOG.next.asciidoc index 73dfe8ff9a4..eb98ef39ded 100644 --- a/x-pack/elastic-agent/CHANGELOG.next.asciidoc +++ b/x-pack/elastic-agent/CHANGELOG.next.asciidoc @@ -14,6 +14,7 @@ - Thread safe sorted set {pull}21290[21290] - Copy Action store on upgrade {pull}21298[21298] - Include inputs in action store actions {pull}21298[21298] +- Fix issue where inputs without processors defined would panic {pull}21628[21628] ==== New features diff --git a/x-pack/elastic-agent/pkg/agent/application/emitter.go b/x-pack/elastic-agent/pkg/agent/application/emitter.go index 3bd06043c30..07f8e1f460b 100644 --- a/x-pack/elastic-agent/pkg/agent/application/emitter.go +++ b/x-pack/elastic-agent/pkg/agent/application/emitter.go @@ -219,17 +219,20 @@ func promoteProcessors(dict *transpiler.Dict) *transpiler.Dict { if p == nil { return dict } + var currentList *transpiler.List current, ok := dict.Find("processors") - currentList, isList := current.Value().(*transpiler.List) - if !isList { - return dict + if ok { + currentList, ok = current.Value().(*transpiler.List) + if !ok { + return dict + } } ast, _ := transpiler.NewAST(map[string]interface{}{ "processors": p, }) procs, _ := transpiler.Lookup(ast, "processors") nodes := nodesFromList(procs.Value().(*transpiler.List)) - if ok { + if ok && currentList != nil { nodes = append(nodes, nodesFromList(currentList)...) } dictNodes := dict.Value().([]transpiler.Node) diff --git a/x-pack/elastic-agent/pkg/agent/application/emitter_test.go b/x-pack/elastic-agent/pkg/agent/application/emitter_test.go index 32770eaa5df..b2286552f9f 100644 --- a/x-pack/elastic-agent/pkg/agent/application/emitter_test.go +++ b/x-pack/elastic-agent/pkg/agent/application/emitter_test.go @@ -479,6 +479,181 @@ func TestRenderInputs(t *testing.T) { }), }, }, + "inputs without processors and vars with processors": { + input: transpiler.NewKey("inputs", transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("type", transpiler.NewStrVal("logfile")), + transpiler.NewKey("streams", transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("paths", transpiler.NewList([]transpiler.Node{ + transpiler.NewStrVal("/var/log/${var1.name}.log"), + })), + }), + })), + }), + })), + expected: transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("type", transpiler.NewStrVal("logfile")), + transpiler.NewKey("streams", transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("paths", transpiler.NewList([]transpiler.Node{ + transpiler.NewStrVal("/var/log/value1.log"), + })), + }), + })), + transpiler.NewKey("processors", transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("add_fields", transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("fields", transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("custom", transpiler.NewStrVal("value1")), + })), + transpiler.NewKey("to", transpiler.NewStrVal("dynamic")), + })), + }), + })), + }), + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("type", transpiler.NewStrVal("logfile")), + transpiler.NewKey("streams", transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("paths", transpiler.NewList([]transpiler.Node{ + transpiler.NewStrVal("/var/log/value2.log"), + })), + }), + })), + transpiler.NewKey("processors", transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("add_fields", transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("fields", transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("custom", transpiler.NewStrVal("value2")), + })), + transpiler.NewKey("to", transpiler.NewStrVal("dynamic")), + })), + }), + })), + }), + }), + varsArray: []*transpiler.Vars{ + mustMakeVarsP(map[string]interface{}{ + "var1": map[string]interface{}{ + "name": "value1", + }, + }, + "var1", + []map[string]interface{}{ + { + "add_fields": map[string]interface{}{ + "fields": map[string]interface{}{ + "custom": "value1", + }, + "to": "dynamic", + }, + }, + }), + mustMakeVarsP(map[string]interface{}{ + "var1": map[string]interface{}{ + "name": "value2", + }, + }, + "var1", + []map[string]interface{}{ + { + "add_fields": map[string]interface{}{ + "fields": map[string]interface{}{ + "custom": "value2", + }, + "to": "dynamic", + }, + }, + }), + }, + }, + "processors incorrectly a map": { + input: transpiler.NewKey("inputs", transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("type", transpiler.NewStrVal("logfile")), + transpiler.NewKey("streams", transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("paths", transpiler.NewList([]transpiler.Node{ + transpiler.NewStrVal("/var/log/${var1.name}.log"), + })), + }), + })), + transpiler.NewKey("processors", transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("add_fields", transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("invalid", transpiler.NewStrVal("value")), + })), + })), + }), + })), + expected: transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("type", transpiler.NewStrVal("logfile")), + transpiler.NewKey("streams", transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("paths", transpiler.NewList([]transpiler.Node{ + transpiler.NewStrVal("/var/log/value1.log"), + })), + }), + })), + transpiler.NewKey("processors", transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("add_fields", transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("invalid", transpiler.NewStrVal("value")), + })), + })), + }), + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("type", transpiler.NewStrVal("logfile")), + transpiler.NewKey("streams", transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("paths", transpiler.NewList([]transpiler.Node{ + transpiler.NewStrVal("/var/log/value2.log"), + })), + }), + })), + transpiler.NewKey("processors", transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("add_fields", transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("invalid", transpiler.NewStrVal("value")), + })), + })), + }), + }), + varsArray: []*transpiler.Vars{ + mustMakeVarsP(map[string]interface{}{ + "var1": map[string]interface{}{ + "name": "value1", + }, + }, + "var1", + []map[string]interface{}{ + { + "add_fields": map[string]interface{}{ + "fields": map[string]interface{}{ + "custom": "value1", + }, + "to": "dynamic", + }, + }, + }), + mustMakeVarsP(map[string]interface{}{ + "var1": map[string]interface{}{ + "name": "value2", + }, + }, + "var1", + []map[string]interface{}{ + { + "add_fields": map[string]interface{}{ + "fields": map[string]interface{}{ + "custom": "value2", + }, + "to": "dynamic", + }, + }, + }), + }, + }, } for name, test := range testcases { From cf11c8b5fcae3e3eed12769308f40878b893ef0f Mon Sep 17 00:00:00 2001 From: DeDe Morton Date: Mon, 12 Oct 2020 11:55:28 -0700 Subject: [PATCH 69/93] Fix conditional coding to remove seccomp info from Winlogbeat (#21652) --- libbeat/docs/shared-securing-beat.asciidoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libbeat/docs/shared-securing-beat.asciidoc b/libbeat/docs/shared-securing-beat.asciidoc index b8dcc3b1957..e1c47d91f2c 100644 --- a/libbeat/docs/shared-securing-beat.asciidoc +++ b/libbeat/docs/shared-securing-beat.asciidoc @@ -29,11 +29,13 @@ For secure communication between APM Server and APM Agents, see <> endif::[] +endif::[] // APM HTTPS information ifdef::beat-specific-security[] @@ -70,5 +72,7 @@ endif::[] // Linux Seccomp ifndef::serverless[] +ifndef::win_only[] include::./security/linux-seccomp.asciidoc[] endif::[] +endif::[] From 9ab0a918237369343cab4e7d17ac09d08e887625 Mon Sep 17 00:00:00 2001 From: Adrian Serrano Date: Tue, 13 Oct 2020 00:43:40 +0200 Subject: [PATCH 70/93] Fix concurrent map read and write in socket dataset (#21690) This fixes a panic caused by a concurrent map read and write in Auditbeat's system/socket dataset. Fixes #21192 --- CHANGELOG.next.asciidoc | 1 + .../auditbeat/module/system/socket/events.go | 4 +-- .../module/system/socket/socket_linux.go | 2 +- .../auditbeat/module/system/socket/state.go | 11 ++++++-- .../module/system/socket/state_test.go | 26 +++++++++++++++++++ 5 files changed, 39 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index cc8fb52396a..f01136c441e 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -195,6 +195,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - auditd: Fix typo in `event.action` of `removed-user-role-from`. {pull}19300[19300] - auditd: Fix typo in `event.action` of `used-suspicious-link`. {pull}19300[19300] - system/socket: Fix kprobe grouping to allow running more than one instance. {pull}20325[20325] +- system/socket: Fixed a crash due to concurrent map read and write. {issue}21192[21192] {pull}21690[21690] *Filebeat* diff --git a/x-pack/auditbeat/module/system/socket/events.go b/x-pack/auditbeat/module/system/socket/events.go index 76d91a8db24..c1937cb0302 100644 --- a/x-pack/auditbeat/module/system/socket/events.go +++ b/x-pack/auditbeat/module/system/socket/events.go @@ -872,8 +872,8 @@ type execveCall struct { creds *commitCreds } -func (e *execveCall) getProcess() process { - p := process{ +func (e *execveCall) getProcess() *process { + p := &process{ pid: e.Meta.PID, path: readCString(e.Path[:]), created: kernelTime(e.Meta.Timestamp), diff --git a/x-pack/auditbeat/module/system/socket/socket_linux.go b/x-pack/auditbeat/module/system/socket/socket_linux.go index 78fdd8ae4ca..11f8a22289e 100644 --- a/x-pack/auditbeat/module/system/socket/socket_linux.go +++ b/x-pack/auditbeat/module/system/socket/socket_linux.go @@ -158,7 +158,7 @@ func (m *MetricSet) Run(r mb.PushReporterV2) { } else { for _, p := range procs { if i, err := p.Info(); err == nil { - process := process{ + process := &process{ name: i.Name, pid: uint32(i.PID), args: i.Args, diff --git a/x-pack/auditbeat/module/system/socket/state.go b/x-pack/auditbeat/module/system/socket/state.go index c7b3ac761a7..fa612b56e1a 100644 --- a/x-pack/auditbeat/module/system/socket/state.go +++ b/x-pack/auditbeat/module/system/socket/state.go @@ -214,6 +214,9 @@ func (f *flow) Timestamp() time.Time { } type process struct { + // RWMutex is used to arbitrate reads and writes to resolvedDomains. + sync.RWMutex + pid uint32 name, path string args []string @@ -229,6 +232,8 @@ type process struct { } func (p *process) addTransaction(tr dns.Transaction) { + p.Lock() + defer p.Unlock() if p.resolvedDomains == nil { p.resolvedDomains = make(map[string]string) } @@ -239,6 +244,8 @@ func (p *process) addTransaction(tr dns.Transaction) { // ResolveIP returns the domain associated with the given IP. func (p *process) ResolveIP(ip net.IP) (domain string, found bool) { + p.RLock() + defer p.RUnlock() domain, found = p.resolvedDomains[ip.String()] return } @@ -542,13 +549,13 @@ func (s *state) ExpireOlder() { s.dns.CleanUp() } -func (s *state) CreateProcess(p process) error { +func (s *state) CreateProcess(p *process) error { if p.pid == 0 { return errors.New("can't create process with PID 0") } s.Lock() defer s.Unlock() - s.processes[p.pid] = &p + s.processes[p.pid] = p if p.createdTime == (time.Time{}) { p.createdTime = s.kernTimestampToTime(p.created) } diff --git a/x-pack/auditbeat/module/system/socket/state_test.go b/x-pack/auditbeat/module/system/socket/state_test.go index 75b73a374d1..89ba0cde9db 100644 --- a/x-pack/auditbeat/module/system/socket/state_test.go +++ b/x-pack/auditbeat/module/system/socket/state_test.go @@ -11,6 +11,7 @@ import ( "fmt" "net" "os" + "sync" "testing" "time" @@ -835,3 +836,28 @@ func TestSocketReuse(t *testing.T) { } assert.Len(t, flows, 1) } + +func TestProcessDNSRace(t *testing.T) { + p := new(process) + var wg sync.WaitGroup + wg.Add(2) + address := func(i byte) net.IP { return net.IPv4(172, 16, 0, i) } + go func() { + for i := byte(255); i > 0; i-- { + p.addTransaction(dns.Transaction{ + Client: net.UDPAddr{IP: net.IPv4(10, 20, 30, 40)}, + Server: net.UDPAddr{IP: net.IPv4(10, 20, 30, 41)}, + Domain: "example.net", + Addresses: []net.IP{address(i)}, + }) + } + wg.Done() + }() + go func() { + for i := byte(255); i > 0; i-- { + p.ResolveIP(address(i)) + } + wg.Done() + }() + wg.Wait() +} From 5e786b383598d252cf200ed22f213d8d5f33ee0d Mon Sep 17 00:00:00 2001 From: Michal Pristas Date: Tue, 13 Oct 2020 09:17:00 +0200 Subject: [PATCH 71/93] [Ingest Manager] Syncing unpacked files (#21706) [Ingest Manager] Syncing unpacked files (#21706) --- .../pkg/artifact/install/tar/tar_installer.go | 6 ++++++ .../pkg/artifact/install/zip/zip_installer.go | 11 +++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/x-pack/elastic-agent/pkg/artifact/install/tar/tar_installer.go b/x-pack/elastic-agent/pkg/artifact/install/tar/tar_installer.go index 5c7f0f593a3..b9e621c71ac 100644 --- a/x-pack/elastic-agent/pkg/artifact/install/tar/tar_installer.go +++ b/x-pack/elastic-agent/pkg/artifact/install/tar/tar_installer.go @@ -102,6 +102,12 @@ func unpack(r io.Reader, dir string) error { if closeErr := wf.Close(); closeErr != nil && err == nil { err = closeErr } + + // sometimes we try executing binary too fast and run into text file busy after unpacking + // syncing prevents this + if syncErr := wf.Sync(); syncErr != nil && err == nil { + err = syncErr + } if err != nil { return fmt.Errorf("TarInstaller: error writing to %s: %v", abs, err) } diff --git a/x-pack/elastic-agent/pkg/artifact/install/zip/zip_installer.go b/x-pack/elastic-agent/pkg/artifact/install/zip/zip_installer.go index ffc90f2dce8..29cdb66f852 100644 --- a/x-pack/elastic-agent/pkg/artifact/install/zip/zip_installer.go +++ b/x-pack/elastic-agent/pkg/artifact/install/zip/zip_installer.go @@ -102,9 +102,16 @@ func (i *Installer) unzip(artifactPath string) error { return err } defer func() { - if cerr := f.Close(); cerr != nil { - err = multierror.Append(err, cerr) + if closeErr := f.Close(); closeErr != nil { + err = multierror.Append(err, closeErr) } + + // sometimes we try executing binary too fast and run into text file busy after unpacking + // syncing prevents this + if syncErr := f.Sync(); syncErr != nil { + err = multierror.Append(err, syncErr) + } + }() if _, err = io.Copy(f, rc); err != nil { From ed772cd8fe1c478318eb5b9622e022412b577df2 Mon Sep 17 00:00:00 2001 From: Michal Pristas Date: Tue, 13 Oct 2020 12:17:29 +0200 Subject: [PATCH 72/93] [Ingest Manager] Change Sync/Close call order (#21735) [Ingest Manager] Change Sync/Close call order (#21735) --- .../pkg/artifact/install/tar/tar_installer.go | 14 +++++++++----- .../pkg/artifact/install/zip/zip_installer.go | 11 ++++------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/x-pack/elastic-agent/pkg/artifact/install/tar/tar_installer.go b/x-pack/elastic-agent/pkg/artifact/install/tar/tar_installer.go index b9e621c71ac..74a74e4c6bc 100644 --- a/x-pack/elastic-agent/pkg/artifact/install/tar/tar_installer.go +++ b/x-pack/elastic-agent/pkg/artifact/install/tar/tar_installer.go @@ -99,15 +99,19 @@ func unpack(r io.Reader, dir string) error { } _, err = io.Copy(wf, tr) + + if err == nil { + // sometimes we try executing binary too fast and run into text file busy after unpacking + // syncing prevents this + if syncErr := wf.Sync(); syncErr != nil { + err = syncErr + } + } + if closeErr := wf.Close(); closeErr != nil && err == nil { err = closeErr } - // sometimes we try executing binary too fast and run into text file busy after unpacking - // syncing prevents this - if syncErr := wf.Sync(); syncErr != nil && err == nil { - err = syncErr - } if err != nil { return fmt.Errorf("TarInstaller: error writing to %s: %v", abs, err) } diff --git a/x-pack/elastic-agent/pkg/artifact/install/zip/zip_installer.go b/x-pack/elastic-agent/pkg/artifact/install/zip/zip_installer.go index 29cdb66f852..b565f630a73 100644 --- a/x-pack/elastic-agent/pkg/artifact/install/zip/zip_installer.go +++ b/x-pack/elastic-agent/pkg/artifact/install/zip/zip_installer.go @@ -105,18 +105,15 @@ func (i *Installer) unzip(artifactPath string) error { if closeErr := f.Close(); closeErr != nil { err = multierror.Append(err, closeErr) } - - // sometimes we try executing binary too fast and run into text file busy after unpacking - // syncing prevents this - if syncErr := f.Sync(); syncErr != nil { - err = multierror.Append(err, syncErr) - } - }() if _, err = io.Copy(f, rc); err != nil { return err } + + // sometimes we try executing binary too fast and run into text file busy after unpacking + // syncing prevents this + f.Sync() } return nil } From 5536fb6cf02480c3db685aa4f27889266cd20a32 Mon Sep 17 00:00:00 2001 From: Chris Mark Date: Tue, 13 Oct 2020 13:28:11 +0300 Subject: [PATCH 73/93] Add istiod metricset (#21519) --- CHANGELOG.next.asciidoc | 1 + .../docs/images/metricbeat-istio-overview.png | Bin 0 -> 187983 bytes metricbeat/docs/modules/istio.asciidoc | 24 +- metricbeat/docs/modules_list.asciidoc | 2 +- x-pack/metricbeat/metricbeat.reference.yml | 7 + .../module/istio/_meta/config.reference.yml | 7 + .../metricbeat/module/istio/_meta/config.yml | 7 + .../module/istio/_meta/docs.asciidoc | 17 +- .../dashboard/Metricbeat-istio-overview.json | 1762 +++++++++++++++++ x-pack/metricbeat/module/istio/fields.go | 2 +- .../module/istio/istiod/_meta/docs.ascoodoc | 4 + .../module/istio/istiod/_meta/fields.yml | 1 + .../istio/istiod/_meta/testdata/config.yml | 5 + .../istiod/_meta/testdata/istiod.v1.7.1.plain | 499 +++++ .../istiod.v1.7.1.plain-expected.json | 843 ++++++++ .../module/istio/istiod/istiod_test.go | 32 + .../module/istio/istiod/manifest.yml | 11 + x-pack/metricbeat/module/istio/module.yaml | 1 - x-pack/metricbeat/module/istio/module.yml | 3 + .../metricbeat/modules.d/istio.yml.disabled | 7 + 20 files changed, 3228 insertions(+), 7 deletions(-) create mode 100644 metricbeat/docs/images/metricbeat-istio-overview.png create mode 100644 x-pack/metricbeat/module/istio/_meta/kibana/7/dashboard/Metricbeat-istio-overview.json create mode 100644 x-pack/metricbeat/module/istio/istiod/_meta/docs.ascoodoc create mode 100644 x-pack/metricbeat/module/istio/istiod/_meta/fields.yml create mode 100644 x-pack/metricbeat/module/istio/istiod/_meta/testdata/config.yml create mode 100644 x-pack/metricbeat/module/istio/istiod/_meta/testdata/istiod.v1.7.1.plain create mode 100644 x-pack/metricbeat/module/istio/istiod/_meta/testdata/istiod.v1.7.1.plain-expected.json create mode 100644 x-pack/metricbeat/module/istio/istiod/istiod_test.go create mode 100644 x-pack/metricbeat/module/istio/istiod/manifest.yml delete mode 100644 x-pack/metricbeat/module/istio/module.yaml create mode 100644 x-pack/metricbeat/module/istio/module.yml diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index f01136c441e..4ca46560ab5 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -453,6 +453,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Add Cloud Foundry tags in related events. {pull}21177[21177] - Cloud Foundry metadata is cached to disk. {pull}20775[20775] - Add option to select the type of index template to load: legacy, component, index. {pull}21212[21212] +- Add istiod metricset. {pull}21519[21519] - Release `add_cloudfoundry_metadata` as GA. {pull}21525[21525] - Add support for OpenStack SSL metadata APIs in `add_cloud_metadata`. {pull}21590[21590] diff --git a/metricbeat/docs/images/metricbeat-istio-overview.png b/metricbeat/docs/images/metricbeat-istio-overview.png new file mode 100644 index 0000000000000000000000000000000000000000..139fe9260d49bd0d0034ad292987f67be37468ce GIT binary patch literal 187983 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�)!0&tU;~T5q=NGJC@b*1d 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 literal 0 HcmV?d00001 diff --git a/metricbeat/docs/modules/istio.asciidoc b/metricbeat/docs/modules/istio.asciidoc index 52be7886e04..bfda2a9588d 100644 --- a/metricbeat/docs/modules/istio.asciidoc +++ b/metricbeat/docs/modules/istio.asciidoc @@ -8,15 +8,28 @@ This file is generated! See scripts/mage/docs_collector.go beta[] -This is the Istio module. The Istio module collects metrics from the +This is the Istio module. +This module is compatible with versions before `1.5` of Istio where microservices architecture is used. If using +versions priot to `1.5` then `mesh`, `mixer`, `pilot`, `galley`, `citadel` metricsets should be used. +wehre the Istio module collects metrics from the Istio https://istio.io/v1.4/docs/tasks/observability/metrics/querying-metrics/#about-the-prometheus-add-on[prometheus exporters endpoints]. +For versions after `1.5`, `istiod` metricset can be used which collects metrics directly from Istio Daemon. The default metricsets are `mesh`, `mixer`, `pilot`, `galley`, `citadel`. [float] === Compatibility -The Istio module is tested with Istio `1.4`. +The Istio module is tested with Istio `1.4` for `mesh`, `mixer`, `pilot`, `galley`, `citadel`. +The Istio module is tested with Istio `1.7` for `istiod`. + +[float] +=== Dashboard + +The Istio module includes a predefined dashboard with overview information about Istio Daemon. +This dashboard is only compatible with versions of Istio after `1.5` which should be monitored with `istiod` metricset. + +image::./images/metricbeat-istio-overview.png[] [float] @@ -62,6 +75,13 @@ metricbeat.modules: period: 10s # use istio-pilot.istio-system:15014, when deploying Metricbeat in a kubernetes cluster as Pod or Daemonset hosts: ["localhost:15014"] + +# Istio istiod to monitor the Istio Daemon for versions after 1.5 of Istio. +- module: istio + metricsets: ['istiod'] + period: 10s + # use istiod.istio-system:15014, when deploying Metricbeat in a kubernetes cluster as Pod or Daemonset + hosts: ['localhost:15014'] ---- [float] diff --git a/metricbeat/docs/modules_list.asciidoc b/metricbeat/docs/modules_list.asciidoc index b37f7f831a3..1bc81071e76 100644 --- a/metricbeat/docs/modules_list.asciidoc +++ b/metricbeat/docs/modules_list.asciidoc @@ -132,7 +132,7 @@ This file is generated! See scripts/mage/docs_collector.go .3+| .3+| |<> beta[] |<> beta[] |<> beta[] -|<> beta[] |image:./images/icon-no.png[No prebuilt dashboards] | +|<> beta[] |image:./images/icon-yes.png[Prebuilt dashboards are available] | .5+| .5+| |<> beta[] |<> beta[] |<> beta[] diff --git a/x-pack/metricbeat/metricbeat.reference.yml b/x-pack/metricbeat/metricbeat.reference.yml index bd99c2cf508..b7b62643353 100644 --- a/x-pack/metricbeat/metricbeat.reference.yml +++ b/x-pack/metricbeat/metricbeat.reference.yml @@ -693,6 +693,13 @@ metricbeat.modules: # use istio-pilot.istio-system:15014, when deploying Metricbeat in a kubernetes cluster as Pod or Daemonset hosts: ["localhost:15014"] +# Istio istiod to monitor the Istio Daemon for versions after 1.5 of Istio. +- module: istio + metricsets: ['istiod'] + period: 10s + # use istiod.istio-system:15014, when deploying Metricbeat in a kubernetes cluster as Pod or Daemonset + hosts: ['localhost:15014'] + #------------------------------- Jolokia Module ------------------------------- - module: jolokia #metricsets: ["jmx"] diff --git a/x-pack/metricbeat/module/istio/_meta/config.reference.yml b/x-pack/metricbeat/module/istio/_meta/config.reference.yml index c3e940f80e5..146728fdfcd 100644 --- a/x-pack/metricbeat/module/istio/_meta/config.reference.yml +++ b/x-pack/metricbeat/module/istio/_meta/config.reference.yml @@ -32,3 +32,10 @@ period: 10s # use istio-pilot.istio-system:15014, when deploying Metricbeat in a kubernetes cluster as Pod or Daemonset hosts: ["localhost:15014"] + +# Istio istiod to monitor the Istio Daemon for versions after 1.5 of Istio. +- module: istio + metricsets: ['istiod'] + period: 10s + # use istiod.istio-system:15014, when deploying Metricbeat in a kubernetes cluster as Pod or Daemonset + hosts: ['localhost:15014'] diff --git a/x-pack/metricbeat/module/istio/_meta/config.yml b/x-pack/metricbeat/module/istio/_meta/config.yml index c3e940f80e5..146728fdfcd 100644 --- a/x-pack/metricbeat/module/istio/_meta/config.yml +++ b/x-pack/metricbeat/module/istio/_meta/config.yml @@ -32,3 +32,10 @@ period: 10s # use istio-pilot.istio-system:15014, when deploying Metricbeat in a kubernetes cluster as Pod or Daemonset hosts: ["localhost:15014"] + +# Istio istiod to monitor the Istio Daemon for versions after 1.5 of Istio. +- module: istio + metricsets: ['istiod'] + period: 10s + # use istiod.istio-system:15014, when deploying Metricbeat in a kubernetes cluster as Pod or Daemonset + hosts: ['localhost:15014'] diff --git a/x-pack/metricbeat/module/istio/_meta/docs.asciidoc b/x-pack/metricbeat/module/istio/_meta/docs.asciidoc index 0a52294d1fc..cfba8ec7ce8 100644 --- a/x-pack/metricbeat/module/istio/_meta/docs.asciidoc +++ b/x-pack/metricbeat/module/istio/_meta/docs.asciidoc @@ -1,9 +1,22 @@ -This is the Istio module. The Istio module collects metrics from the +This is the Istio module. +This module is compatible with versions before `1.5` of Istio where microservices architecture is used. If using +versions priot to `1.5` then `mesh`, `mixer`, `pilot`, `galley`, `citadel` metricsets should be used. +wehre the Istio module collects metrics from the Istio https://istio.io/v1.4/docs/tasks/observability/metrics/querying-metrics/#about-the-prometheus-add-on[prometheus exporters endpoints]. +For versions after `1.5`, `istiod` metricset can be used which collects metrics directly from Istio Daemon. The default metricsets are `mesh`, `mixer`, `pilot`, `galley`, `citadel`. [float] === Compatibility -The Istio module is tested with Istio `1.4`. +The Istio module is tested with Istio `1.4` for `mesh`, `mixer`, `pilot`, `galley`, `citadel`. +The Istio module is tested with Istio `1.7` for `istiod`. + +[float] +=== Dashboard + +The Istio module includes a predefined dashboard with overview information about Istio Daemon. +This dashboard is only compatible with versions of Istio after `1.5` which should be monitored with `istiod` metricset. + +image::./images/metricbeat-istio-overview.png[] diff --git a/x-pack/metricbeat/module/istio/_meta/kibana/7/dashboard/Metricbeat-istio-overview.json b/x-pack/metricbeat/module/istio/_meta/kibana/7/dashboard/Metricbeat-istio-overview.json new file mode 100644 index 00000000000..72b995c2382 --- /dev/null +++ b/x-pack/metricbeat/module/istio/_meta/kibana/7/dashboard/Metricbeat-istio-overview.json @@ -0,0 +1,1762 @@ +{ + "objects": [ + { + "attributes": { + "description": "Overview of the Istiod Service status", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "optionsJSON": { + "hidePanelTitles": false, + "useMargins": true + }, + "panelsJSON": [ + { + "embeddableConfig": { + "title": "Pilot Proxy Queue Time" + }, + "gridData": { + "h": 9, + "i": "cd1bbc4f-95de-4156-a3ef-c091cf6402c0", + "w": 12, + "x": 0, + "y": 0 + }, + "panelIndex": "cd1bbc4f-95de-4156-a3ef-c091cf6402c0", + "panelRefName": "panel_0", + "title": "Pilot Proxy Queue Time", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Pilot xds Push Time" + }, + "gridData": { + "h": 9, + "i": "06af11e6-e026-48db-a06b-b34b402b535b", + "w": 12, + "x": 12, + "y": 0 + }, + "panelIndex": "06af11e6-e026-48db-a06b-b34b402b535b", + "panelRefName": "panel_1", + "title": "Pilot xds Push Time", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Pilot xds Pushes" + }, + "gridData": { + "h": 9, + "i": "d9a49bf0-f88b-4d4f-a1e2-74fbd482f77c", + "w": 11, + "x": 24, + "y": 0 + }, + "panelIndex": "d9a49bf0-f88b-4d4f-a1e2-74fbd482f77c", + "panelRefName": "panel_2", + "title": "Pilot xds Pushes", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Pilot Inbound Updates" + }, + "gridData": { + "h": 9, + "i": "a8e47ef0-03db-419f-890f-0880d674682c", + "w": 13, + "x": 35, + "y": 0 + }, + "panelIndex": "a8e47ef0-03db-419f-890f-0880d674682c", + "panelRefName": "panel_3", + "title": "Pilot Inbound Updates", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Citadel Cert Issuane" + }, + "gridData": { + "h": 9, + "i": "e708abfa-5a95-483c-9bb2-4470ee913f3c", + "w": 12, + "x": 0, + "y": 9 + }, + "panelIndex": "e708abfa-5a95-483c-9bb2-4470ee913f3c", + "panelRefName": "panel_4", + "title": "Citadel Cert Issuane", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Galley Validation Failed" + }, + "gridData": { + "h": 9, + "i": "724f0f9e-2186-4ddd-859c-edb2649b8c0f", + "w": 12, + "x": 12, + "y": 9 + }, + "panelIndex": "724f0f9e-2186-4ddd-859c-edb2649b8c0f", + "panelRefName": "panel_5", + "title": "Galley Validation Failed", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Pods witout IP", + "vis": null + }, + "gridData": { + "h": 9, + "i": "32eaa989-a4f9-4d31-97cb-684f31488aa8", + "w": 8, + "x": 24, + "y": 9 + }, + "panelIndex": "32eaa989-a4f9-4d31-97cb-684f31488aa8", + "panelRefName": "panel_6", + "title": "Pods witout IP", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Pilot Virtual Services", + "vis": null + }, + "gridData": { + "h": 9, + "i": "6a8463fe-b7cb-4cd8-bf01-f7ca6a185178", + "w": 8, + "x": 32, + "y": 9 + }, + "panelIndex": "6a8463fe-b7cb-4cd8-bf01-f7ca6a185178", + "panelRefName": "panel_7", + "title": "Pilot Virtual Services", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Pilot Services", + "vis": null + }, + "gridData": { + "h": 9, + "i": "51ecc2f8-3c3f-4a80-b4b6-b52db10e68ad", + "w": 8, + "x": 40, + "y": 9 + }, + "panelIndex": "51ecc2f8-3c3f-4a80-b4b6-b52db10e68ad", + "panelRefName": "panel_8", + "title": "Pilot Services", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Pilot Conflict Inbound Listener", + "vis": null + }, + "gridData": { + "h": 9, + "i": "0a63d980-8d93-4ce1-b5a1-ab77e589ceec", + "w": 9, + "x": 0, + "y": 18 + }, + "panelIndex": "0a63d980-8d93-4ce1-b5a1-ab77e589ceec", + "panelRefName": "panel_9", + "title": "Pilot Conflict Inbound Listener", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Pilot eds instances", + "vis": null + }, + "gridData": { + "h": 9, + "i": "9fbfca4c-37b5-4a1a-924e-49fc9ef2294c", + "w": 10, + "x": 9, + "y": 18 + }, + "panelIndex": "9fbfca4c-37b5-4a1a-924e-49fc9ef2294c", + "panelRefName": "panel_10", + "title": "Pilot eds instances", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Pilot xds endpoints" + }, + "gridData": { + "h": 9, + "i": "d6b2845f-9582-4863-853e-ab753f3d763e", + "w": 14, + "x": 19, + "y": 18 + }, + "panelIndex": "d6b2845f-9582-4863-853e-ab753f3d763e", + "panelRefName": "panel_11", + "title": "Pilot xds endpoints", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Pilot xds expired" + }, + "gridData": { + "h": 9, + "i": "3d0dec37-26c3-490b-a45f-48b6d1baa160", + "w": 15, + "x": 33, + "y": 18 + }, + "panelIndex": "3d0dec37-26c3-490b-a45f-48b6d1baa160", + "panelRefName": "panel_12", + "title": "Pilot xds expired", + "version": "7.8.0" + } + ], + "timeRestore": false, + "title": "[Metricbeat Istio] Overview", + "version": 1 + }, + "id": "d899d3f0-0883-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "dashboard": "7.3.0" + }, + "references": [ + { + "id": "dd1392f0-07d8-11eb-a3fd-1b45ec532bb3", + "name": "panel_0", + "type": "visualization" + }, + { + "id": "b5b3abb0-087c-11eb-a3fd-1b45ec532bb3", + "name": "panel_1", + "type": "visualization" + }, + { + "id": "f858c200-087e-11eb-a3fd-1b45ec532bb3", + "name": "panel_2", + "type": "visualization" + }, + { + "id": "aa997510-087d-11eb-a3fd-1b45ec532bb3", + "name": "panel_3", + "type": "visualization" + }, + { + "id": "506c8490-087f-11eb-a3fd-1b45ec532bb3", + "name": "panel_4", + "type": "visualization" + }, + { + "id": "98b01f00-087f-11eb-a3fd-1b45ec532bb3", + "name": "panel_5", + "type": "visualization" + }, + { + "id": "4275f710-0882-11eb-a3fd-1b45ec532bb3", + "name": "panel_6", + "type": "visualization" + }, + { + "id": "96bfe060-0882-11eb-a3fd-1b45ec532bb3", + "name": "panel_7", + "type": "visualization" + }, + { + "id": "6cfbe3f0-0882-11eb-a3fd-1b45ec532bb3", + "name": "panel_8", + "type": "visualization" + }, + { + "id": "d62a1e60-0881-11eb-a3fd-1b45ec532bb3", + "name": "panel_9", + "type": "visualization" + }, + { + "id": "12cdcce0-0882-11eb-a3fd-1b45ec532bb3", + "name": "panel_10", + "type": "visualization" + }, + { + "id": "e5f3e870-0882-11eb-a3fd-1b45ec532bb3", + "name": "panel_11", + "type": "visualization" + }, + { + "id": "0ed17c80-0883-11eb-a3fd-1b45ec532bb3", + "name": "panel_12", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2020-10-07T10:23:52.518Z", + "version": "WzQ0OTIsMV0=" + }, + { + "attributes": { + "description": "Time in seconds, a proxy is in the push queue before being dequeued.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Pilot Proxy Queue Time [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "7ccbe640-07d8-11eb-985d-2f490d4c2901", + "index_pattern": "metricbeat-*", + "interval": "auto", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#6092C0", + "fill": 0, + "formatter": "number", + "id": "7ccbe641-07d8-11eb-985d-2f490d4c2901", + "label": "queue_time", + "line_width": 2, + "metrics": [ + { + "field": "prometheus.pilot_proxy_queue_time.histogram.values", + "id": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "percentiles": [ + { + "id": "88c0d000-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "25" + }, + { + "id": "03ef6580-0887-11eb-876a-9d8e5e94d2f5", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "50" + }, + { + "id": "071fe4f0-0887-11eb-876a-9d8e5e94d2f5", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "75" + }, + { + "id": "0b7164c0-0887-11eb-876a-9d8e5e94d2f5", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "90" + }, + { + "id": "0f611580-0887-11eb-876a-9d8e5e94d2f5", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "95" + }, + { + "id": "136f98e0-0887-11eb-876a-9d8e5e94d2f5", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "99" + } + ], + "type": "percentile" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_mode": "everything", + "stacked": "none", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Pilot Proxy Queue Time [Metricbeat Istio]", + "type": "metrics" + } + }, + "id": "dd1392f0-07d8-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-10-07T10:23:11.367Z", + "version": "WzQ0ODMsMV0=" + }, + { + "attributes": { + "description": "Total time in seconds Pilot takes to push lds, rds, cds and eds.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Pilot xds Push Time [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "7ccbe640-07d8-11eb-985d-2f490d4c2901", + "index_pattern": "metricbeat-*", + "interval": "auto", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#6092C0", + "fill": 0, + "filter": { + "language": "kuery", + "query": "prometheus.labels.type: \"rds\" OR prometheus.labels.type: \"lds\" OR prometheus.labels.type: \"cds\" OR prometheus.labels.type: \"eds\"" + }, + "formatter": "s,s,", + "id": "7ccbe641-07d8-11eb-985d-2f490d4c2901", + "label": "pilot_xds_push_time", + "line_width": 2, + "metrics": [ + { + "field": "prometheus.pilot_xds_push_time.histogram.values", + "id": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "percentiles": [ + { + "id": "88c0d000-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "25" + }, + { + "id": "95c750d0-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "50" + }, + { + "id": "9c5ec900-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "75" + }, + { + "id": "9f8e3700-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "95" + }, + { + "id": "a3581040-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "99" + } + ], + "type": "percentile" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_mode": "terms", + "stacked": "none", + "terms_field": "prometheus.labels.type", + "terms_size": "20", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Pilot xds Push Time [Metricbeat Istio]", + "type": "metrics" + } + }, + "id": "b5b3abb0-087c-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-10-07T10:23:48.176Z", + "version": "WzQ0ODgsMV0=" + }, + { + "attributes": { + "description": "Pilot build and send errors for lds, rds, cds and eds.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Pilot xds Pushes [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "7ccbe640-07d8-11eb-985d-2f490d4c2901", + "index_pattern": "metricbeat-*", + "interval": "auto", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#6092C0", + "fill": 0, + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "7ccbe641-07d8-11eb-985d-2f490d4c2901", + "label": "pilot_xds_pushes", + "line_width": 2, + "metrics": [ + { + "field": "prometheus.pilot_xds_pushes.counter", + "id": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "percentiles": [ + { + "id": "88c0d000-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "50" + }, + { + "id": "95c750d0-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "25" + }, + { + "id": "9c5ec900-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "75" + }, + { + "id": "9f8e3700-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "95" + }, + { + "id": "a3581040-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "99" + } + ], + "type": "max", + "unit": "" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_mode": "terms", + "stacked": "none", + "terms_field": "prometheus.labels.type", + "terms_order_by": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "terms_size": "4", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Pilot xds Pushes [Metricbeat Istio]", + "type": "metrics" + } + }, + "id": "f858c200-087e-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-10-07T09:24:59.040Z", + "version": "WzQzOTAsMV0=" + }, + { + "attributes": { + "description": "Total number of updates received by pilot.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Pilot Inbound Updates [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "7ccbe640-07d8-11eb-985d-2f490d4c2901", + "index_pattern": "metricbeat-*", + "interval": "auto", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#6092C0", + "fill": 0, + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "7ccbe641-07d8-11eb-985d-2f490d4c2901", + "label": "pilot_inbound_updates", + "line_width": 2, + "metrics": [ + { + "field": "prometheus.pilot_inbound_updates.counter", + "id": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "percentiles": [ + { + "id": "88c0d000-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "50" + }, + { + "id": "95c750d0-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "25" + }, + { + "id": "9c5ec900-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "75" + }, + { + "id": "9f8e3700-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "95" + }, + { + "id": "a3581040-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "99" + } + ], + "type": "max", + "unit": "" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_mode": "terms", + "stacked": "none", + "terms_field": "prometheus.labels.type", + "terms_order_by": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "terms_size": "10", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Pilot Inbound Updates [Metricbeat Istio]", + "type": "metrics" + } + }, + "id": "aa997510-087d-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-10-07T09:21:43.250Z", + "version": "WzQzODcsMV0=" + }, + { + "attributes": { + "description": "The number of certificates issuances that have succeeded.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Citadel Cert Issuance [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "7ccbe640-07d8-11eb-985d-2f490d4c2901", + "index_pattern": "metricbeat-*", + "interval": "auto", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#6092C0", + "fill": 0, + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "7ccbe641-07d8-11eb-985d-2f490d4c2901", + "label": "success_cert_issuance", + "line_width": 2, + "metrics": [ + { + "field": "prometheus.citadel_server_success_cert_issuance_count.counter", + "id": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "percentiles": [ + { + "id": "88c0d000-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "50" + }, + { + "id": "95c750d0-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "25" + }, + { + "id": "9c5ec900-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "75" + }, + { + "id": "9f8e3700-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "95" + }, + { + "id": "a3581040-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "99" + } + ], + "type": "max", + "unit": "" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_mode": "everything", + "stacked": "none", + "terms_field": "prometheus.labels.type", + "terms_order_by": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "terms_size": "4", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Citadel Cert Issuance [Metricbeat Istio]", + "type": "metrics" + } + }, + "id": "506c8490-087f-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-10-07T10:15:51.172Z", + "version": "WzQ0NzYsMV0=" + }, + { + "attributes": { + "description": "Resource validation failed.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Galley Validation Failed [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "7ccbe640-07d8-11eb-985d-2f490d4c2901", + "index_pattern": "metricbeat-*", + "interval": "auto", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#6092C0", + "fill": 0, + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "7ccbe641-07d8-11eb-985d-2f490d4c2901", + "label": "galley_validation_failed", + "line_width": 2, + "metrics": [ + { + "field": "prometheus.galley_validation_failed.counter", + "id": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "percentiles": [ + { + "id": "88c0d000-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "50" + }, + { + "id": "95c750d0-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "25" + }, + { + "id": "9c5ec900-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "75" + }, + { + "id": "9f8e3700-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "95" + }, + { + "id": "a3581040-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "99" + } + ], + "type": "max", + "unit": "" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_mode": "terms", + "stacked": "none", + "terms_field": "prometheus.labels.resource", + "terms_order_by": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "terms_size": "10", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Galley Validation Failed [Metricbeat Istio]", + "type": "metrics" + } + }, + "id": "98b01f00-087f-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-10-07T09:30:05.212Z", + "version": "WzQzOTksMV0=" + }, + { + "attributes": { + "description": "Pods not found in the endpoint table, possibly invalid.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.index", + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Pilot Pods without IP [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": { + "customLabel": "Pilot No IP pods", + "field": "prometheus.pilot_no_ip.value" + }, + "schema": "metric", + "type": "avg" + } + ], + "params": { + "addLegend": true, + "addTooltip": true, + "gauge": { + "alignment": "automatic", + "backStyle": "Full", + "colorSchema": "Green to Red", + "colorsRange": [ + { + "from": 0, + "to": 50 + }, + { + "from": 50, + "to": 75 + }, + { + "from": 75, + "to": 100 + } + ], + "extendRange": true, + "gaugeColorMode": "Labels", + "gaugeStyle": "Full", + "gaugeType": "Arc", + "invertColors": false, + "labels": { + "color": "black", + "show": true + }, + "orientation": "vertical", + "percentageMode": false, + "scale": { + "color": "rgba(105,112,125,0.2)", + "labels": false, + "show": true + }, + "style": { + "bgColor": true, + "bgFill": "rgba(105,112,125,0.2)", + "bgMask": false, + "bgWidth": 0.9, + "fontSize": 60, + "mask": false, + "maskBars": 50, + "subText": "", + "width": 0.9 + }, + "type": "meter" + }, + "isDisplayWarning": false, + "type": "gauge" + }, + "title": "Pilot Pods without IP [Metricbeat Istio]", + "type": "gauge" + } + }, + "id": "4275f710-0882-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [ + { + "id": "metricbeat-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2020-10-07T09:48:31.873Z", + "version": "WzQ0MjcsMV0=" + }, + { + "attributes": { + "description": "Total virtual services known to pilot.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.index", + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Pilot Virtual Services [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": { + "customLabel": "Pilot Virtual Services", + "field": "prometheus.pilot_virt_services.value" + }, + "schema": "metric", + "type": "avg" + } + ], + "params": { + "addLegend": true, + "addTooltip": true, + "gauge": { + "alignment": "automatic", + "backStyle": "Full", + "colorSchema": "Green to Red", + "colorsRange": [ + { + "from": 0, + "to": 50 + }, + { + "from": 50, + "to": 75 + }, + { + "from": 75, + "to": 100 + } + ], + "extendRange": true, + "gaugeColorMode": "Labels", + "gaugeStyle": "Full", + "gaugeType": "Arc", + "invertColors": false, + "labels": { + "color": "black", + "show": true + }, + "orientation": "vertical", + "percentageMode": false, + "scale": { + "color": "rgba(105,112,125,0.2)", + "labels": false, + "show": true + }, + "style": { + "bgColor": true, + "bgFill": "rgba(105,112,125,0.2)", + "bgMask": false, + "bgWidth": 0.9, + "fontSize": 60, + "mask": false, + "maskBars": 50, + "subText": "", + "width": 0.9 + }, + "type": "meter" + }, + "isDisplayWarning": false, + "type": "gauge" + }, + "title": "Pilot Virtual Services [Metricbeat Istio]", + "type": "gauge" + } + }, + "id": "96bfe060-0882-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [ + { + "id": "metricbeat-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2020-10-07T09:50:53.286Z", + "version": "WzQ0MzMsMV0=" + }, + { + "attributes": { + "description": "Total services known to pilot.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.index", + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Pilot Services [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": { + "customLabel": "Pilot Services", + "field": "prometheus.pilot_services.value" + }, + "schema": "metric", + "type": "avg" + } + ], + "params": { + "addLegend": true, + "addTooltip": true, + "gauge": { + "alignment": "automatic", + "backStyle": "Full", + "colorSchema": "Green to Red", + "colorsRange": [ + { + "from": 0, + "to": 50 + }, + { + "from": 50, + "to": 75 + }, + { + "from": 75, + "to": 100 + } + ], + "extendRange": true, + "gaugeColorMode": "Labels", + "gaugeStyle": "Full", + "gaugeType": "Arc", + "invertColors": false, + "labels": { + "color": "black", + "show": true + }, + "orientation": "vertical", + "percentageMode": false, + "scale": { + "color": "rgba(105,112,125,0.2)", + "labels": false, + "show": true + }, + "style": { + "bgColor": true, + "bgFill": "rgba(105,112,125,0.2)", + "bgMask": false, + "bgWidth": 0.9, + "fontSize": 60, + "mask": false, + "maskBars": 50, + "subText": "", + "width": 0.9 + }, + "type": "meter" + }, + "isDisplayWarning": false, + "type": "gauge" + }, + "title": "Pilot Services [Metricbeat Istio]", + "type": "gauge" + } + }, + "id": "6cfbe3f0-0882-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [ + { + "id": "metricbeat-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2020-10-07T10:04:27.677Z", + "version": "WzQ0NTksMV0=" + }, + { + "attributes": { + "description": "Number of conflicting inbound listeners.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.index", + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Pilot Conflict Inbound Listener [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": { + "customLabel": "Pilot conflict inbound listener", + "field": "prometheus.pilot_conflict_inbound_listener.value" + }, + "schema": "metric", + "type": "avg" + } + ], + "params": { + "addLegend": true, + "addTooltip": true, + "gauge": { + "alignment": "automatic", + "backStyle": "Full", + "colorSchema": "Green to Red", + "colorsRange": [ + { + "from": 0, + "to": 50 + }, + { + "from": 50, + "to": 75 + }, + { + "from": 75, + "to": 100 + } + ], + "extendRange": true, + "gaugeColorMode": "Labels", + "gaugeStyle": "Full", + "gaugeType": "Arc", + "invertColors": false, + "labels": { + "color": "black", + "show": true + }, + "orientation": "vertical", + "percentageMode": false, + "scale": { + "color": "rgba(105,112,125,0.2)", + "labels": false, + "show": true + }, + "style": { + "bgColor": true, + "bgFill": "rgba(105,112,125,0.2)", + "bgMask": false, + "bgWidth": 0.9, + "fontSize": 60, + "mask": false, + "maskBars": 50, + "subText": "", + "width": 0.9 + }, + "type": "meter" + }, + "isDisplayWarning": false, + "type": "gauge" + }, + "title": "Pilot Conflict Inbound Listener [Metricbeat Istio]", + "type": "gauge" + } + }, + "id": "d62a1e60-0881-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [ + { + "id": "metricbeat-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2020-10-07T09:45:30.182Z", + "version": "WzQ0MjAsMV0=" + }, + { + "attributes": { + "description": "Number of clusters without instances.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.index", + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Pilot eds Instances [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": { + "customLabel": "Pilot eds instnaces", + "field": "prometheus.pilot_eds_no_instances.value" + }, + "schema": "metric", + "type": "avg" + } + ], + "params": { + "addLegend": true, + "addTooltip": true, + "gauge": { + "alignment": "automatic", + "backStyle": "Full", + "colorSchema": "Green to Red", + "colorsRange": [ + { + "from": 0, + "to": 50 + }, + { + "from": 50, + "to": 75 + }, + { + "from": 75, + "to": 100 + } + ], + "extendRange": true, + "gaugeColorMode": "Labels", + "gaugeStyle": "Full", + "gaugeType": "Arc", + "invertColors": false, + "labels": { + "color": "black", + "show": true + }, + "orientation": "vertical", + "percentageMode": false, + "scale": { + "color": "rgba(105,112,125,0.2)", + "labels": false, + "show": true + }, + "style": { + "bgColor": true, + "bgFill": "rgba(105,112,125,0.2)", + "bgMask": false, + "bgWidth": 0.9, + "fontSize": 60, + "mask": false, + "maskBars": 50, + "subText": "", + "width": 0.9 + }, + "type": "meter" + }, + "isDisplayWarning": false, + "type": "gauge" + }, + "title": "Pilot eds Instances [Metricbeat Istio]", + "type": "gauge" + } + }, + "id": "12cdcce0-0882-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [ + { + "id": "metricbeat-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2020-10-07T09:47:11.918Z", + "version": "WzQ0MjQsMV0=" + }, + { + "attributes": { + "description": "Number of endpoints connected to this pilot using XDS.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Pilot XDS endpoints [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "7ccbe640-07d8-11eb-985d-2f490d4c2901", + "index_pattern": "metricbeat-*", + "interval": "auto", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#6092C0", + "fill": 0, + "formatter": "number", + "id": "7ccbe641-07d8-11eb-985d-2f490d4c2901", + "label": "pilot_xds", + "line_width": 2, + "metrics": [ + { + "field": "prometheus.pilot_xds.value", + "id": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "percentiles": [ + { + "id": "88c0d000-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "50" + }, + { + "id": "95c750d0-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "25" + }, + { + "id": "9c5ec900-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "75" + }, + { + "id": "9f8e3700-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "95" + }, + { + "id": "a3581040-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "99" + } + ], + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_mode": "everything", + "stacked": "none", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Pilot XDS endpoints [Metricbeat Istio]", + "type": "metrics" + } + }, + "id": "e5f3e870-0882-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-10-07T09:53:38.583Z", + "version": "WzQ0NDEsMV0=" + }, + { + "attributes": { + "description": "Total number of XDS requests with an expired nonce.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Pilot XDS expired [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "7ccbe640-07d8-11eb-985d-2f490d4c2901", + "index_pattern": "metricbeat-*", + "interval": "auto", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#6092C0", + "fill": 0, + "formatter": "number", + "id": "7ccbe641-07d8-11eb-985d-2f490d4c2901", + "label": "pilot_xds_expired_nonce", + "line_width": 2, + "metrics": [ + { + "field": "prometheus.pilot_xds_expired_nonce.counter", + "id": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "percentiles": [ + { + "id": "88c0d000-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "50" + }, + { + "id": "95c750d0-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "25" + }, + { + "id": "9c5ec900-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "75" + }, + { + "id": "9f8e3700-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "95" + }, + { + "id": "a3581040-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "99" + } + ], + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_mode": "everything", + "stacked": "none", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Pilot XDS expired [Metricbeat Istio]", + "type": "metrics" + } + }, + "id": "0ed17c80-0883-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-10-07T09:54:14.728Z", + "version": "WzQ0NDMsMV0=" + } + ], + "version": "7.8.0" +} diff --git a/x-pack/metricbeat/module/istio/fields.go b/x-pack/metricbeat/module/istio/fields.go index 40a9baed3f5..ccd41e55fe8 100644 --- a/x-pack/metricbeat/module/istio/fields.go +++ b/x-pack/metricbeat/module/istio/fields.go @@ -19,5 +19,5 @@ func init() { // AssetIstio returns asset data. // This is the base64 encoded gzipped contents of module/istio. func AssetIstio() string { - return "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" + return "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" } diff --git a/x-pack/metricbeat/module/istio/istiod/_meta/docs.ascoodoc b/x-pack/metricbeat/module/istio/istiod/_meta/docs.ascoodoc new file mode 100644 index 00000000000..61170c60413 --- /dev/null +++ b/x-pack/metricbeat/module/istio/istiod/_meta/docs.ascoodoc @@ -0,0 +1,4 @@ +This is the istiod metricset of the module istio. +This metricset collects metrics from the Istio Daemon of Istio versions higher than 1.5 + +Tested with Istio 1.7 diff --git a/x-pack/metricbeat/module/istio/istiod/_meta/fields.yml b/x-pack/metricbeat/module/istio/istiod/_meta/fields.yml new file mode 100644 index 00000000000..8033a27f5ac --- /dev/null +++ b/x-pack/metricbeat/module/istio/istiod/_meta/fields.yml @@ -0,0 +1 @@ +- release: beta diff --git a/x-pack/metricbeat/module/istio/istiod/_meta/testdata/config.yml b/x-pack/metricbeat/module/istio/istiod/_meta/testdata/config.yml new file mode 100644 index 00000000000..376691991b5 --- /dev/null +++ b/x-pack/metricbeat/module/istio/istiod/_meta/testdata/config.yml @@ -0,0 +1,5 @@ +type: http +url: "/metrics" +suffix: plain +remove_fields_from_comparison: ["prometheus.labels.instance"] +omit_documented_fields_check: ["prometheus.labels.*"] diff --git a/x-pack/metricbeat/module/istio/istiod/_meta/testdata/istiod.v1.7.1.plain b/x-pack/metricbeat/module/istio/istiod/_meta/testdata/istiod.v1.7.1.plain new file mode 100644 index 00000000000..4ec26bf4a2c --- /dev/null +++ b/x-pack/metricbeat/module/istio/istiod/_meta/testdata/istiod.v1.7.1.plain @@ -0,0 +1,499 @@ +# HELP citadel_server_csr_count The number of CSRs received by Citadel server. +# TYPE citadel_server_csr_count counter +citadel_server_csr_count 8 +# HELP citadel_server_root_cert_expiry_timestamp The unix timestamp, in seconds, when Citadel root cert will expire. A negative time indicates the cert is expired. +# TYPE citadel_server_root_cert_expiry_timestamp gauge +citadel_server_root_cert_expiry_timestamp 1.916309303e+09 +# HELP citadel_server_success_cert_issuance_count The number of certificates issuances that have succeeded. +# TYPE citadel_server_success_cert_issuance_count counter +citadel_server_success_cert_issuance_count 8 +# HELP endpoint_no_pod Endpoints without an associated pod. +# TYPE endpoint_no_pod gauge +endpoint_no_pod 0 +# HELP galley_validation_config_updates k8s webhook configuration updates +# TYPE galley_validation_config_updates counter +galley_validation_config_updates 2 +# HELP galley_validation_failed Resource validation failed +# TYPE galley_validation_failed counter +galley_validation_failed{group="networking.istio.io",reason="invalid_resource",resource="gateways",version="v1alpha3"} 1 +# HELP go_gc_duration_seconds A summary of the GC invocation durations. +# TYPE go_gc_duration_seconds summary +go_gc_duration_seconds{quantile="0"} 8.969e-06 +go_gc_duration_seconds{quantile="0.25"} 8.7539e-05 +go_gc_duration_seconds{quantile="0.5"} 0.000238037 +go_gc_duration_seconds{quantile="0.75"} 0.000783504 +go_gc_duration_seconds{quantile="1"} 0.005515511 +go_gc_duration_seconds_sum 0.013316174 +go_gc_duration_seconds_count 13 +# HELP go_goroutines Number of goroutines that currently exist. +# TYPE go_goroutines gauge +go_goroutines 345 +# HELP go_info Information about the Go environment. +# TYPE go_info gauge +go_info{version="go1.14.7"} 1 +# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use. +# TYPE go_memstats_alloc_bytes gauge +go_memstats_alloc_bytes 3.1723136e+07 +# HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed. +# TYPE go_memstats_alloc_bytes_total counter +go_memstats_alloc_bytes_total 1.106604e+08 +# HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table. +# TYPE go_memstats_buck_hash_sys_bytes gauge +go_memstats_buck_hash_sys_bytes 1.496287e+06 +# HELP go_memstats_frees_total Total number of frees. +# TYPE go_memstats_frees_total counter +go_memstats_frees_total 982627 +# HELP go_memstats_gc_cpu_fraction The fraction of this program's available CPU time used by the GC since the program started. +# TYPE go_memstats_gc_cpu_fraction gauge +go_memstats_gc_cpu_fraction 6.904294960464036e-05 +# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata. +# TYPE go_memstats_gc_sys_bytes gauge +go_memstats_gc_sys_bytes 3.832072e+06 +# HELP go_memstats_heap_alloc_bytes Number of heap bytes allocated and still in use. +# TYPE go_memstats_heap_alloc_bytes gauge +go_memstats_heap_alloc_bytes 3.1723136e+07 +# HELP go_memstats_heap_idle_bytes Number of heap bytes waiting to be used. +# TYPE go_memstats_heap_idle_bytes gauge +go_memstats_heap_idle_bytes 2.1651456e+07 +# HELP go_memstats_heap_inuse_bytes Number of heap bytes that are in use. +# TYPE go_memstats_heap_inuse_bytes gauge +go_memstats_heap_inuse_bytes 4.2508288e+07 +# HELP go_memstats_heap_objects Number of allocated objects. +# TYPE go_memstats_heap_objects gauge +go_memstats_heap_objects 157553 +# HELP go_memstats_heap_released_bytes Number of heap bytes released to OS. +# TYPE go_memstats_heap_released_bytes gauge +go_memstats_heap_released_bytes 1.7825792e+07 +# HELP go_memstats_heap_sys_bytes Number of heap bytes obtained from system. +# TYPE go_memstats_heap_sys_bytes gauge +go_memstats_heap_sys_bytes 6.4159744e+07 +# HELP go_memstats_last_gc_time_seconds Number of seconds since 1970 of last garbage collection. +# TYPE go_memstats_last_gc_time_seconds gauge +go_memstats_last_gc_time_seconds 1.6019028053800597e+09 +# HELP go_memstats_lookups_total Total number of pointer lookups. +# TYPE go_memstats_lookups_total counter +go_memstats_lookups_total 0 +# HELP go_memstats_mallocs_total Total number of mallocs. +# TYPE go_memstats_mallocs_total counter +go_memstats_mallocs_total 1.14018e+06 +# HELP go_memstats_mcache_inuse_bytes Number of bytes in use by mcache structures. +# TYPE go_memstats_mcache_inuse_bytes gauge +go_memstats_mcache_inuse_bytes 6944 +# HELP go_memstats_mcache_sys_bytes Number of bytes used for mcache structures obtained from system. +# TYPE go_memstats_mcache_sys_bytes gauge +go_memstats_mcache_sys_bytes 16384 +# HELP go_memstats_mspan_inuse_bytes Number of bytes in use by mspan structures. +# TYPE go_memstats_mspan_inuse_bytes gauge +go_memstats_mspan_inuse_bytes 485384 +# HELP go_memstats_mspan_sys_bytes Number of bytes used for mspan structures obtained from system. +# TYPE go_memstats_mspan_sys_bytes gauge +go_memstats_mspan_sys_bytes 573440 +# HELP go_memstats_next_gc_bytes Number of heap bytes when next garbage collection will take place. +# TYPE go_memstats_next_gc_bytes gauge +go_memstats_next_gc_bytes 5.3766592e+07 +# HELP go_memstats_other_sys_bytes Number of bytes used for other system allocations. +# TYPE go_memstats_other_sys_bytes gauge +go_memstats_other_sys_bytes 914201 +# HELP go_memstats_stack_inuse_bytes Number of bytes in use by the stack allocator. +# TYPE go_memstats_stack_inuse_bytes gauge +go_memstats_stack_inuse_bytes 2.94912e+06 +# HELP go_memstats_stack_sys_bytes Number of bytes obtained from system for stack allocator. +# TYPE go_memstats_stack_sys_bytes gauge +go_memstats_stack_sys_bytes 2.94912e+06 +# HELP go_memstats_sys_bytes Number of bytes obtained from system. +# TYPE go_memstats_sys_bytes gauge +go_memstats_sys_bytes 7.3941248e+07 +# HELP go_threads Number of OS threads created. +# TYPE go_threads gauge +go_threads 14 +# HELP grpc_server_handled_total Total number of RPCs completed on the server, regardless of success or failure. +# TYPE grpc_server_handled_total counter +grpc_server_handled_total{grpc_code="Aborted",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="Aborted",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Aborted",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Aborted",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Aborted",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Aborted",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="AlreadyExists",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="AlreadyExists",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="AlreadyExists",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="AlreadyExists",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="AlreadyExists",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="AlreadyExists",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Canceled",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="Canceled",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Canceled",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Canceled",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Canceled",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Canceled",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="DataLoss",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="DataLoss",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="DataLoss",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="DataLoss",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="DataLoss",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="DataLoss",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="DeadlineExceeded",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="DeadlineExceeded",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="DeadlineExceeded",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="DeadlineExceeded",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="DeadlineExceeded",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="DeadlineExceeded",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="FailedPrecondition",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="FailedPrecondition",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="FailedPrecondition",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="FailedPrecondition",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="FailedPrecondition",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="FailedPrecondition",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Internal",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="Internal",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Internal",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Internal",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Internal",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Internal",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="InvalidArgument",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="InvalidArgument",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="InvalidArgument",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="InvalidArgument",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="InvalidArgument",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="InvalidArgument",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="NotFound",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="NotFound",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="NotFound",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="NotFound",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="NotFound",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="NotFound",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="OK",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 8 +grpc_server_handled_total{grpc_code="OK",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="OK",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="OK",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="OK",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="OK",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="OutOfRange",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="OutOfRange",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="OutOfRange",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="OutOfRange",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="OutOfRange",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="OutOfRange",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="PermissionDenied",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="PermissionDenied",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="PermissionDenied",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="PermissionDenied",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="PermissionDenied",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="PermissionDenied",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="ResourceExhausted",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="ResourceExhausted",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="ResourceExhausted",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="ResourceExhausted",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="ResourceExhausted",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="ResourceExhausted",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unauthenticated",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="Unauthenticated",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unauthenticated",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unauthenticated",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unauthenticated",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unauthenticated",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unavailable",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="Unavailable",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unavailable",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unavailable",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unavailable",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unavailable",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unimplemented",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="Unimplemented",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unimplemented",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unimplemented",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unimplemented",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unimplemented",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unknown",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="Unknown",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unknown",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unknown",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unknown",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unknown",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +# HELP grpc_server_handling_seconds Histogram of response latency (seconds) of gRPC that had been application-level handled by the server. +# TYPE grpc_server_handling_seconds histogram +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="0.005"} 7 +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="0.01"} 8 +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="0.025"} 8 +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="0.05"} 8 +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="0.1"} 8 +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="0.25"} 8 +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="0.5"} 8 +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="1"} 8 +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="2.5"} 8 +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="5"} 8 +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="10"} 8 +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="+Inf"} 8 +grpc_server_handling_seconds_sum{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0.031283576 +grpc_server_handling_seconds_count{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 8 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.005"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.01"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.025"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.05"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.1"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.25"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="1"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="2.5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="10"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="+Inf"} 0 +grpc_server_handling_seconds_sum{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handling_seconds_count{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.005"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.01"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.025"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.05"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.1"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.25"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="1"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="2.5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="10"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="+Inf"} 0 +grpc_server_handling_seconds_sum{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handling_seconds_count{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="0.005"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="0.01"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="0.025"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="0.05"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="0.1"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="0.25"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="0.5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="1"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="2.5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="10"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="+Inf"} 0 +grpc_server_handling_seconds_sum{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handling_seconds_count{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.005"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.01"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.025"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.05"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.1"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.25"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="1"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="2.5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="10"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="+Inf"} 0 +grpc_server_handling_seconds_sum{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handling_seconds_count{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.005"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.01"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.025"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.05"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.1"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.25"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="1"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="2.5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="10"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="+Inf"} 0 +grpc_server_handling_seconds_sum{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handling_seconds_count{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +# HELP grpc_server_msg_received_total Total number of RPC stream messages received on the server. +# TYPE grpc_server_msg_received_total counter +grpc_server_msg_received_total{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 8 +grpc_server_msg_received_total{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_msg_received_total{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_msg_received_total{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_msg_received_total{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_msg_received_total{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +# HELP grpc_server_msg_sent_total Total number of gRPC stream messages sent by the server. +# TYPE grpc_server_msg_sent_total counter +grpc_server_msg_sent_total{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 8 +grpc_server_msg_sent_total{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_msg_sent_total{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_msg_sent_total{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_msg_sent_total{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_msg_sent_total{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +# HELP grpc_server_started_total Total number of RPCs started on the server. +# TYPE grpc_server_started_total counter +grpc_server_started_total{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 8 +grpc_server_started_total{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_started_total{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_started_total{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_started_total{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_started_total{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +# HELP istio_build Istio component build info +# TYPE istio_build gauge +istio_build{component="pilot",tag="1.7.1"} 1 +# HELP pilot_conflict_inbound_listener Number of conflicting inbound listeners. +# TYPE pilot_conflict_inbound_listener gauge +pilot_conflict_inbound_listener 0 +# HELP pilot_conflict_outbound_listener_http_over_current_tcp Number of conflicting wildcard http listeners with current wildcard tcp listener. +# TYPE pilot_conflict_outbound_listener_http_over_current_tcp gauge +pilot_conflict_outbound_listener_http_over_current_tcp 0 +# HELP pilot_conflict_outbound_listener_tcp_over_current_http Number of conflicting wildcard tcp listeners with current wildcard http listener. +# TYPE pilot_conflict_outbound_listener_tcp_over_current_http gauge +pilot_conflict_outbound_listener_tcp_over_current_http 0 +# HELP pilot_conflict_outbound_listener_tcp_over_current_tcp Number of conflicting tcp listeners with current tcp listener. +# TYPE pilot_conflict_outbound_listener_tcp_over_current_tcp gauge +pilot_conflict_outbound_listener_tcp_over_current_tcp 0 +# HELP pilot_destrule_subsets Duplicate subsets across destination rules for same host +# TYPE pilot_destrule_subsets gauge +pilot_destrule_subsets 0 +# HELP pilot_duplicate_envoy_clusters Duplicate envoy clusters caused by service entries with same hostname +# TYPE pilot_duplicate_envoy_clusters gauge +pilot_duplicate_envoy_clusters 0 +# HELP pilot_eds_no_instances Number of clusters without instances. +# TYPE pilot_eds_no_instances gauge +pilot_eds_no_instances 0 +# HELP pilot_endpoint_not_ready Endpoint found in unready state. +# TYPE pilot_endpoint_not_ready gauge +pilot_endpoint_not_ready 0 +# HELP pilot_inbound_updates Total number of updates received by pilot. +# TYPE pilot_inbound_updates counter +pilot_inbound_updates{type="config"} 90 +pilot_inbound_updates{type="eds"} 54 +pilot_inbound_updates{type="svc"} 22 +# HELP pilot_info Pilot version and build information. +# TYPE pilot_info gauge +pilot_info{version="1.7.1-4e26c697ce460dc8d3b25b25818fb0163ca16394-Clean"} 1 +# HELP pilot_k8s_cfg_events Events from k8s config. +# TYPE pilot_k8s_cfg_events counter +pilot_k8s_cfg_events{event="add",type="DestinationRule"} 4 +pilot_k8s_cfg_events{event="add",type="EnvoyFilter"} 8 +pilot_k8s_cfg_events{event="add",type="Gateway"} 1 +pilot_k8s_cfg_events{event="add",type="VirtualService"} 1 +# HELP pilot_k8s_reg_events Events from k8s registry. +# TYPE pilot_k8s_reg_events counter +pilot_k8s_reg_events{event="add",type="Endpoints"} 12 +pilot_k8s_reg_events{event="add",type="Nodes"} 1 +pilot_k8s_reg_events{event="add",type="Pods"} 22 +pilot_k8s_reg_events{event="add",type="Services"} 11 +pilot_k8s_reg_events{event="update",type="Endpoints"} 30 +pilot_k8s_reg_events{event="update",type="Nodes"} 1 +pilot_k8s_reg_events{event="update",type="Pods"} 61 +pilot_k8s_reg_events{event="updatesame",type="Endpoints"} 185 +# HELP pilot_no_ip Pods not found in the endpoint table, possibly invalid. +# TYPE pilot_no_ip gauge +pilot_no_ip 0 +# HELP pilot_proxy_convergence_time Delay in seconds between config change and a proxy receiving all required configuration. +# TYPE pilot_proxy_convergence_time histogram +pilot_proxy_convergence_time_bucket{le="0.1"} 1 +pilot_proxy_convergence_time_bucket{le="0.5"} 1 +pilot_proxy_convergence_time_bucket{le="1"} 1 +pilot_proxy_convergence_time_bucket{le="3"} 1 +pilot_proxy_convergence_time_bucket{le="5"} 1 +pilot_proxy_convergence_time_bucket{le="10"} 1 +pilot_proxy_convergence_time_bucket{le="20"} 1 +pilot_proxy_convergence_time_bucket{le="30"} 1 +pilot_proxy_convergence_time_bucket{le="+Inf"} 1 +pilot_proxy_convergence_time_sum 0.002034992 +pilot_proxy_convergence_time_count 1 +# HELP pilot_proxy_queue_time Time in seconds, a proxy is in the push queue before being dequeued. +# TYPE pilot_proxy_queue_time histogram +pilot_proxy_queue_time_bucket{le="0.1"} 41 +pilot_proxy_queue_time_bucket{le="1"} 41 +pilot_proxy_queue_time_bucket{le="3"} 41 +pilot_proxy_queue_time_bucket{le="5"} 41 +pilot_proxy_queue_time_bucket{le="10"} 41 +pilot_proxy_queue_time_bucket{le="20"} 41 +pilot_proxy_queue_time_bucket{le="30"} 41 +pilot_proxy_queue_time_bucket{le="+Inf"} 41 +pilot_proxy_queue_time_sum 0.002216352 +pilot_proxy_queue_time_count 41 +# HELP pilot_push_triggers Total number of times a push was triggered, labeled by reason for the push. +# TYPE pilot_push_triggers counter +pilot_push_triggers{type="endpoint"} 40 +pilot_push_triggers{type="proxy"} 1 +# HELP pilot_services Total services known to pilot. +# TYPE pilot_services gauge +pilot_services 11 +# HELP pilot_virt_services Total virtual services known to pilot. +# TYPE pilot_virt_services gauge +pilot_virt_services 1 +# HELP pilot_vservice_dup_domain Virtual services with dup domains. +# TYPE pilot_vservice_dup_domain gauge +pilot_vservice_dup_domain 0 +# HELP pilot_xds Number of endpoints connected to this pilot using XDS. +# TYPE pilot_xds gauge +pilot_xds{version="1.7.1"} 8 +# HELP pilot_xds_push_time Total time in seconds Pilot takes to push lds, rds, cds and eds. +# TYPE pilot_xds_push_time histogram +pilot_xds_push_time_bucket{type="cds",le="0.01"} 9 +pilot_xds_push_time_bucket{type="cds",le="0.1"} 9 +pilot_xds_push_time_bucket{type="cds",le="1"} 9 +pilot_xds_push_time_bucket{type="cds",le="3"} 9 +pilot_xds_push_time_bucket{type="cds",le="5"} 9 +pilot_xds_push_time_bucket{type="cds",le="10"} 9 +pilot_xds_push_time_bucket{type="cds",le="20"} 9 +pilot_xds_push_time_bucket{type="cds",le="30"} 9 +pilot_xds_push_time_bucket{type="cds",le="+Inf"} 9 +pilot_xds_push_time_sum{type="cds"} 0.009549363999999998 +pilot_xds_push_time_count{type="cds"} 9 +pilot_xds_push_time_bucket{type="eds",le="0.01"} 49 +pilot_xds_push_time_bucket{type="eds",le="0.1"} 49 +pilot_xds_push_time_bucket{type="eds",le="1"} 49 +pilot_xds_push_time_bucket{type="eds",le="3"} 49 +pilot_xds_push_time_bucket{type="eds",le="5"} 49 +pilot_xds_push_time_bucket{type="eds",le="10"} 49 +pilot_xds_push_time_bucket{type="eds",le="20"} 49 +pilot_xds_push_time_bucket{type="eds",le="30"} 49 +pilot_xds_push_time_bucket{type="eds",le="+Inf"} 49 +pilot_xds_push_time_sum{type="eds"} 0.008623654 +pilot_xds_push_time_count{type="eds"} 49 +pilot_xds_push_time_bucket{type="lds",le="0.01"} 9 +pilot_xds_push_time_bucket{type="lds",le="0.1"} 9 +pilot_xds_push_time_bucket{type="lds",le="1"} 9 +pilot_xds_push_time_bucket{type="lds",le="3"} 9 +pilot_xds_push_time_bucket{type="lds",le="5"} 9 +pilot_xds_push_time_bucket{type="lds",le="10"} 9 +pilot_xds_push_time_bucket{type="lds",le="20"} 9 +pilot_xds_push_time_bucket{type="lds",le="30"} 9 +pilot_xds_push_time_bucket{type="lds",le="+Inf"} 9 +pilot_xds_push_time_sum{type="lds"} 0.011886486000000002 +pilot_xds_push_time_count{type="lds"} 9 +pilot_xds_push_time_bucket{type="rds",le="0.01"} 9 +pilot_xds_push_time_bucket{type="rds",le="0.1"} 9 +pilot_xds_push_time_bucket{type="rds",le="1"} 9 +pilot_xds_push_time_bucket{type="rds",le="3"} 9 +pilot_xds_push_time_bucket{type="rds",le="5"} 9 +pilot_xds_push_time_bucket{type="rds",le="10"} 9 +pilot_xds_push_time_bucket{type="rds",le="20"} 9 +pilot_xds_push_time_bucket{type="rds",le="30"} 9 +pilot_xds_push_time_bucket{type="rds",le="+Inf"} 9 +pilot_xds_push_time_sum{type="rds"} 0.002646734 +pilot_xds_push_time_count{type="rds"} 9 +# HELP pilot_xds_pushes Pilot build and send errors for lds, rds, cds and eds. +# TYPE pilot_xds_pushes counter +pilot_xds_pushes{type="cds"} 9 +pilot_xds_pushes{type="eds"} 49 +pilot_xds_pushes{type="lds"} 9 +pilot_xds_pushes{type="rds"} 9 +# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds. +# TYPE process_cpu_seconds_total counter +process_cpu_seconds_total 2.35 +# HELP process_max_fds Maximum number of open file descriptors. +# TYPE process_max_fds gauge +process_max_fds 1.048576e+06 +# HELP process_open_fds Number of open file descriptors. +# TYPE process_open_fds gauge +process_open_fds 52 +# HELP process_resident_memory_bytes Resident memory size in bytes. +# TYPE process_resident_memory_bytes gauge +process_resident_memory_bytes 1.08478464e+08 +# HELP process_start_time_seconds Start time of the process since unix epoch in seconds. +# TYPE process_start_time_seconds gauge +process_start_time_seconds 1.60190238401e+09 +# HELP process_virtual_memory_bytes Virtual memory size in bytes. +# TYPE process_virtual_memory_bytes gauge +process_virtual_memory_bytes 8.08730624e+08 +# HELP process_virtual_memory_max_bytes Maximum amount of virtual memory available in bytes. +# TYPE process_virtual_memory_max_bytes gauge +process_virtual_memory_max_bytes -1 +# HELP statsd_metric_mapper_cache_gets_total The count of total metric cache gets. +# TYPE statsd_metric_mapper_cache_gets_total counter +statsd_metric_mapper_cache_gets_total 0 +# HELP statsd_metric_mapper_cache_hits_total The count of total metric cache hits. +# TYPE statsd_metric_mapper_cache_hits_total counter +statsd_metric_mapper_cache_hits_total 0 +# HELP statsd_metric_mapper_cache_length The count of unique metrics currently cached. +# TYPE statsd_metric_mapper_cache_length gauge +statsd_metric_mapper_cache_length 0 diff --git a/x-pack/metricbeat/module/istio/istiod/_meta/testdata/istiod.v1.7.1.plain-expected.json b/x-pack/metricbeat/module/istio/istiod/_meta/testdata/istiod.v1.7.1.plain-expected.json new file mode 100644 index 00000000000..2d96ee6b90e --- /dev/null +++ b/x-pack/metricbeat/module/istio/istiod/_meta/testdata/istiod.v1.7.1.plain-expected.json @@ -0,0 +1,843 @@ +[ + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "add", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "Gateway" + }, + "pilot_k8s_cfg_events": { + "counter": 1, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "add", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "Nodes" + }, + "pilot_k8s_reg_events": { + "counter": 1, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "update", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "Nodes" + }, + "pilot_k8s_reg_events": { + "counter": 1, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "add", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "DestinationRule" + }, + "pilot_k8s_cfg_events": { + "counter": 4, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "proxy" + }, + "pilot_push_triggers": { + "counter": 1, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "lds" + }, + "pilot_xds_push_time": { + "histogram": { + "counts": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "values": [ + 0.005, + 0.05500000000000001, + 0.55, + 2, + 4, + 7.5, + 15, + 25, + 40 + ] + } + }, + "pilot_xds_pushes": { + "counter": 9, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "svc" + }, + "pilot_inbound_updates": { + "counter": 22, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "eds" + }, + "pilot_inbound_updates": { + "counter": 54, + "rate": 0 + }, + "pilot_xds_push_time": { + "histogram": { + "counts": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "values": [ + 0.005, + 0.05500000000000001, + 0.55, + 2, + 4, + 7.5, + 15, + 25, + 40 + ] + } + }, + "pilot_xds_pushes": { + "counter": 49, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "add", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "EnvoyFilter" + }, + "pilot_k8s_cfg_events": { + "counter": 8, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "endpoint" + }, + "pilot_push_triggers": { + "counter": 40, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "instance": "127.0.0.1:51143", + "job": "istio", + "version": "1.7.1" + }, + "pilot_xds": { + "value": 8 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "add", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "Services" + }, + "pilot_k8s_reg_events": { + "counter": 11, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "update", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "Pods" + }, + "pilot_k8s_reg_events": { + "counter": 61, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "update", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "Endpoints" + }, + "pilot_k8s_reg_events": { + "counter": 30, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "instance": "127.0.0.1:51143", + "job": "istio", + "version": "1.7.1-4e26c697ce460dc8d3b25b25818fb0163ca16394-Clean" + }, + "pilot_info": { + "value": 1 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "add", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "Endpoints" + }, + "pilot_k8s_reg_events": { + "counter": 12, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "add", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "VirtualService" + }, + "pilot_k8s_cfg_events": { + "counter": 1, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "cds" + }, + "pilot_xds_push_time": { + "histogram": { + "counts": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "values": [ + 0.005, + 0.05500000000000001, + 0.55, + 2, + 4, + 7.5, + 15, + 25, + 40 + ] + } + }, + "pilot_xds_pushes": { + "counter": 9, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "add", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "Pods" + }, + "pilot_k8s_reg_events": { + "counter": 22, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "updatesame", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "Endpoints" + }, + "pilot_k8s_reg_events": { + "counter": 185, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "config" + }, + "pilot_inbound_updates": { + "counter": 90, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "citadel_server_csr_count": { + "counter": 8, + "rate": 0 + }, + "citadel_server_root_cert_expiry_timestamp": { + "value": 1916309303 + }, + "citadel_server_success_cert_issuance_count": { + "counter": 8, + "rate": 0 + }, + "galley_validation_config_updates": { + "counter": 2, + "rate": 0 + }, + "labels": { + "instance": "127.0.0.1:51143", + "job": "istio" + }, + "pilot_conflict_inbound_listener": { + "value": 0 + }, + "pilot_conflict_outbound_listener_http_over_current_tcp": { + "value": 0 + }, + "pilot_conflict_outbound_listener_tcp_over_current_http": { + "value": 0 + }, + "pilot_conflict_outbound_listener_tcp_over_current_tcp": { + "value": 0 + }, + "pilot_destrule_subsets": { + "value": 0 + }, + "pilot_duplicate_envoy_clusters": { + "value": 0 + }, + "pilot_eds_no_instances": { + "value": 0 + }, + "pilot_endpoint_not_ready": { + "value": 0 + }, + "pilot_no_ip": { + "value": 0 + }, + "pilot_proxy_convergence_time": { + "histogram": { + "counts": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "values": [ + 0.05, + 0.30000000000000004, + 0.75, + 2, + 4, + 7.5, + 15, + 25, + 40 + ] + } + }, + "pilot_proxy_queue_time": { + "histogram": { + "counts": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "values": [ + 0.05, + 0.55, + 2, + 4, + 7.5, + 15, + 25, + 40 + ] + } + }, + "pilot_services": { + "value": 11 + }, + "pilot_virt_services": { + "value": 1 + }, + "pilot_vservice_dup_domain": { + "value": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "rds" + }, + "pilot_xds_push_time": { + "histogram": { + "counts": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "values": [ + 0.005, + 0.05500000000000001, + 0.55, + 2, + 4, + 7.5, + 15, + 25, + 40 + ] + } + }, + "pilot_xds_pushes": { + "counter": 9, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "galley_validation_failed": { + "counter": 1, + "rate": 0 + }, + "labels": { + "group": "networking.istio.io", + "instance": "127.0.0.1:51143", + "job": "istio", + "reason": "invalid_resource", + "resource": "gateways", + "version": "v1alpha3" + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + } +] \ No newline at end of file diff --git a/x-pack/metricbeat/module/istio/istiod/istiod_test.go b/x-pack/metricbeat/module/istio/istiod/istiod_test.go new file mode 100644 index 00000000000..23ce86ca7f0 --- /dev/null +++ b/x-pack/metricbeat/module/istio/istiod/istiod_test.go @@ -0,0 +1,32 @@ +// 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. + +// +build !integration + +package istiod + +import ( + "os" + "testing" + + "github.com/elastic/beats/v7/libbeat/logp" + "github.com/elastic/beats/v7/metricbeat/mb" + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + + // Register input module and metricset + _ "github.com/elastic/beats/v7/x-pack/metricbeat/module/prometheus" + _ "github.com/elastic/beats/v7/x-pack/metricbeat/module/prometheus/collector" +) + +func init() { + // To be moved to some kind of helper + os.Setenv("BEAT_STRICT_PERMS", "false") + mb.Registry.SetSecondarySource(mb.NewLightModulesSource("../../../module")) +} + +func TestEventMapping(t *testing.T) { + logp.TestingSetup() + + mbtest.TestDataFiles(t, "istio", "istiod") +} diff --git a/x-pack/metricbeat/module/istio/istiod/manifest.yml b/x-pack/metricbeat/module/istio/istiod/manifest.yml new file mode 100644 index 00000000000..0a8294d9192 --- /dev/null +++ b/x-pack/metricbeat/module/istio/istiod/manifest.yml @@ -0,0 +1,11 @@ +default: false +input: + module: prometheus + metricset: collector + defaults: + metrics_path: /metrics + metrics_filters: + include: ["citadel_*", "galley_*", "pilot_*"] + exclude: ["^up$"] + use_types: true + rate_counters: true diff --git a/x-pack/metricbeat/module/istio/module.yaml b/x-pack/metricbeat/module/istio/module.yaml deleted file mode 100644 index 8b137891791..00000000000 --- a/x-pack/metricbeat/module/istio/module.yaml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/x-pack/metricbeat/module/istio/module.yml b/x-pack/metricbeat/module/istio/module.yml new file mode 100644 index 00000000000..de29503fca5 --- /dev/null +++ b/x-pack/metricbeat/module/istio/module.yml @@ -0,0 +1,3 @@ +name: istio +metricsets: +- istiod diff --git a/x-pack/metricbeat/modules.d/istio.yml.disabled b/x-pack/metricbeat/modules.d/istio.yml.disabled index 1140d047a09..b65bcdef949 100644 --- a/x-pack/metricbeat/modules.d/istio.yml.disabled +++ b/x-pack/metricbeat/modules.d/istio.yml.disabled @@ -35,3 +35,10 @@ period: 10s # use istio-pilot.istio-system:15014, when deploying Metricbeat in a kubernetes cluster as Pod or Daemonset hosts: ["localhost:15014"] + +# Istio istiod to monitor the Istio Daemon for versions after 1.5 of Istio. +- module: istio + metricsets: ['istiod'] + period: 10s + # use istiod.istio-system:15014, when deploying Metricbeat in a kubernetes cluster as Pod or Daemonset + hosts: ['localhost:15014'] From a3fe79648592169bf81d482f5d3895a5045d140a Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Tue, 13 Oct 2020 13:08:46 +0200 Subject: [PATCH 74/93] Fix leaks with metadata processors (#16349) Add a closer interface for processors so their resources can be released when the processor is not needed anymore. Explicitly close publisher pipelines so their processors are closed. Add closers for add_docker_metadata, add_kubernetes_metadata and add_process_metadata. Script processor will fail if a processor that needs to be closed is used. --- CHANGELOG.next.asciidoc | 1 + libbeat/beat/pipeline.go | 1 + libbeat/cmd/instance/beat.go | 5 + libbeat/common/docker/watcher.go | 7 +- .../add_docker_metadata.go | 12 ++ .../add_docker_metadata_integration_test.go | 120 ++++++++++++++++++ .../add_docker_metadata_test.go | 1 + .../add_kubernetes_metadata/cache.go | 32 +++-- .../add_kubernetes_metadata/kubernetes.go | 10 ++ .../add_process_metadata.go | 46 +++++-- .../add_process_metadata_test.go | 2 +- libbeat/processors/processor.go | 30 +++++ .../javascript/module/processor/chain.go | 11 ++ .../module/processor/processor_test.go | 54 ++++++-- libbeat/publisher/pipeline/client.go | 10 ++ libbeat/publisher/processing/default.go | 12 +- libbeat/publisher/processing/default_test.go | 72 +++++++++++ libbeat/publisher/processing/processing.go | 2 + libbeat/publisher/processing/processors.go | 16 +++ libbeat/tests/docker/docker.go | 20 +++ 20 files changed, 426 insertions(+), 38 deletions(-) create mode 100644 libbeat/processors/add_docker_metadata/add_docker_metadata_integration_test.go diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 4ca46560ab5..576c0062310 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -180,6 +180,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Explicitly detect missing variables in autodiscover configuration, log them at the debug level. {issue}20568[20568] {pull}20898[20898] - Fix `libbeat.output.write.bytes` and `libbeat.output.read.bytes` metrics of the Elasticsearch output. {issue}20752[20752] {pull}21197[21197] - The `o365input` and `o365` module now recover from an authentication problem or other fatal errors, instead of terminating. {pull}21259[21258] +- Orderly close processors when processing pipelines are not needed anymore to release their resources. {pull}16349[16349] *Auditbeat* diff --git a/libbeat/beat/pipeline.go b/libbeat/beat/pipeline.go index 699c96d8b62..f13b3c39ff2 100644 --- a/libbeat/beat/pipeline.go +++ b/libbeat/beat/pipeline.go @@ -138,6 +138,7 @@ type ClientEventer interface { type ProcessorList interface { Processor + Close() error All() []Processor } diff --git a/libbeat/cmd/instance/beat.go b/libbeat/cmd/instance/beat.go index ca58b9b321f..714b266ea84 100644 --- a/libbeat/cmd/instance/beat.go +++ b/libbeat/cmd/instance/beat.go @@ -371,6 +371,11 @@ func (b *Beat) launch(settings Settings, bt beat.Creator) error { if err != nil { return err } + defer func() { + if err := b.processing.Close(); err != nil { + logp.Warn("Failed to close global processing: %v", err) + } + }() // Windows: Mark service as stopped. // After this is run, a Beat service is considered by the OS to be stopped diff --git a/libbeat/common/docker/watcher.go b/libbeat/common/docker/watcher.go index 0543d37e9c3..2421c232eee 100644 --- a/libbeat/common/docker/watcher.go +++ b/libbeat/common/docker/watcher.go @@ -138,6 +138,7 @@ func NewWatcher(log *logp.Logger, host string, tls *TLSConfig, storeShortID bool // Extra check to confirm that Docker is available _, err = client.Info(context.Background()) if err != nil { + client.Close() return nil, err } @@ -395,14 +396,12 @@ func (w *watcher) cleanupWorker() { log := w.log for { - // Wait a full period - time.Sleep(w.cleanupTimeout) - select { case <-w.ctx.Done(): w.stopped.Done() return - default: + // Wait a full period + case <-time.After(w.cleanupTimeout): // Check entries for timeout var toDelete []string timeout := time.Now().Add(-w.cleanupTimeout) diff --git a/libbeat/processors/add_docker_metadata/add_docker_metadata.go b/libbeat/processors/add_docker_metadata/add_docker_metadata.go index cf0fda79d8d..beaca3bb46b 100644 --- a/libbeat/processors/add_docker_metadata/add_docker_metadata.go +++ b/libbeat/processors/add_docker_metadata/add_docker_metadata.go @@ -209,6 +209,18 @@ func (d *addDockerMetadata) Run(event *beat.Event) (*beat.Event, error) { return event, nil } +func (d *addDockerMetadata) Close() error { + if d.cgroups != nil { + d.cgroups.StopJanitor() + } + d.watcher.Stop() + err := processors.Close(d.sourceProcessor) + if err != nil { + return errors.Wrap(err, "closing source processor of add_docker_metadata") + } + return nil +} + func (d *addDockerMetadata) String() string { return fmt.Sprintf("%v=[match_fields=[%v] match_pids=[%v]]", processorName, strings.Join(d.fields, ", "), strings.Join(d.pidFields, ", ")) diff --git a/libbeat/processors/add_docker_metadata/add_docker_metadata_integration_test.go b/libbeat/processors/add_docker_metadata/add_docker_metadata_integration_test.go new file mode 100644 index 00000000000..91d3315a401 --- /dev/null +++ b/libbeat/processors/add_docker_metadata/add_docker_metadata_integration_test.go @@ -0,0 +1,120 @@ +// 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. + +// +build linux darwin windows +// +build integration + +package add_docker_metadata + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/elastic/beats/v7/libbeat/beat" + "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/common/docker" + "github.com/elastic/beats/v7/libbeat/logp" + "github.com/elastic/beats/v7/libbeat/processors" + dockertest "github.com/elastic/beats/v7/libbeat/tests/docker" + "github.com/elastic/beats/v7/libbeat/tests/resources" +) + +func TestAddDockerMetadata(t *testing.T) { + goroutines := resources.NewGoroutinesChecker() + defer goroutines.Check(t) + + client, err := docker.NewClient(defaultConfig().Host, nil, nil) + require.NoError(t, err) + + // Docker clients can affect the goroutines checker because they keep + // idle keep-alive connections, so we explicitly close them. + // These idle connections in principle wouldn't represent leaks even if + // the client is not explicitly closed because they are eventually closed. + defer client.Close() + + // Start a container to have some data to enrich events + testClient, err := dockertest.NewClient() + require.NoError(t, err) + // Explicitly close client to don't affect goroutines checker + defer testClient.Close() + + image := "busybox" + cmd := []string{"sleep", "60"} + labels := map[string]string{"label": "foo"} + id, err := testClient.ContainerStart(image, cmd, labels) + require.NoError(t, err) + defer testClient.ContainerRemove(id) + + info, err := testClient.ContainerInspect(id) + require.NoError(t, err) + pid := info.State.Pid + + config, err := common.NewConfigFrom(map[string]interface{}{ + "match_fields": []string{"cid"}, + }) + watcherConstructor := newWatcherWith(client) + processor, err := buildDockerMetadataProcessor(logp.L(), config, watcherConstructor) + require.NoError(t, err) + + t.Run("match container by container id", func(t *testing.T) { + input := &beat.Event{Fields: common.MapStr{ + "cid": id, + }} + result, err := processor.Run(input) + require.NoError(t, err) + + resultLabels, _ := result.Fields.GetValue("container.labels") + expectedLabels := common.MapStr{"label": "foo"} + assert.Equal(t, expectedLabels, resultLabels) + assert.Equal(t, id, result.Fields["cid"]) + }) + + t.Run("match container by process id", func(t *testing.T) { + input := &beat.Event{Fields: common.MapStr{ + "cid": id, + "process.pid": pid, + }} + result, err := processor.Run(input) + require.NoError(t, err) + + resultLabels, _ := result.Fields.GetValue("container.labels") + expectedLabels := common.MapStr{"label": "foo"} + assert.Equal(t, expectedLabels, resultLabels) + assert.Equal(t, id, result.Fields["cid"]) + }) + + t.Run("don't enrich non existing container", func(t *testing.T) { + input := &beat.Event{Fields: common.MapStr{ + "cid": "notexists", + }} + result, err := processor.Run(input) + require.NoError(t, err) + assert.Equal(t, input.Fields, result.Fields) + }) + + err = processors.Close(processor) + require.NoError(t, err) +} + +func newWatcherWith(client docker.Client) docker.WatcherConstructor { + return func(log *logp.Logger, host string, tls *docker.TLSConfig, storeShortID bool) (docker.Watcher, error) { + return docker.NewWatcherWithClient(log, client, 60*time.Second, storeShortID) + } +} diff --git a/libbeat/processors/add_docker_metadata/add_docker_metadata_test.go b/libbeat/processors/add_docker_metadata/add_docker_metadata_test.go index 2d8a5a9e970..f246d597e13 100644 --- a/libbeat/processors/add_docker_metadata/add_docker_metadata_test.go +++ b/libbeat/processors/add_docker_metadata/add_docker_metadata_test.go @@ -16,6 +16,7 @@ // under the License. // +build linux darwin windows +// +build !integration package add_docker_metadata diff --git a/libbeat/processors/add_kubernetes_metadata/cache.go b/libbeat/processors/add_kubernetes_metadata/cache.go index da7492b43a9..5de52efdc1b 100644 --- a/libbeat/processors/add_kubernetes_metadata/cache.go +++ b/libbeat/processors/add_kubernetes_metadata/cache.go @@ -31,6 +31,7 @@ type cache struct { timeout time.Duration deleted map[string]time.Time // key -> when should this obj be deleted metadata map[string]common.MapStr + done chan struct{} } func newCache(cleanupTimeout time.Duration) *cache { @@ -38,6 +39,7 @@ func newCache(cleanupTimeout time.Duration) *cache { timeout: cleanupTimeout, deleted: make(map[string]time.Time), metadata: make(map[string]common.MapStr), + done: make(chan struct{}), } go c.cleanup() return c @@ -67,15 +69,29 @@ func (c *cache) set(key string, data common.MapStr) { } func (c *cache) cleanup() { - ticker := time.Tick(timeout) - for now := range ticker { - c.Lock() - for k, t := range c.deleted { - if now.After(t) { - delete(c.deleted, k) - delete(c.metadata, k) + if timeout <= 0 { + return + } + + ticker := time.NewTicker(timeout) + defer ticker.Stop() + for { + select { + case <-c.done: + return + case now := <-ticker.C: + c.Lock() + for k, t := range c.deleted { + if now.After(t) { + delete(c.deleted, k) + delete(c.metadata, k) + } } + c.Unlock() } - c.Unlock() } } + +func (c *cache) stop() { + close(c.done) +} diff --git a/libbeat/processors/add_kubernetes_metadata/kubernetes.go b/libbeat/processors/add_kubernetes_metadata/kubernetes.go index 2a5f4d2faed..535eb1187ea 100644 --- a/libbeat/processors/add_kubernetes_metadata/kubernetes.go +++ b/libbeat/processors/add_kubernetes_metadata/kubernetes.go @@ -242,6 +242,16 @@ func (k *kubernetesAnnotator) Run(event *beat.Event) (*beat.Event, error) { return event, nil } +func (k *kubernetesAnnotator) Close() error { + if k.watcher != nil { + k.watcher.Stop() + } + if k.cache != nil { + k.cache.stop() + } + return nil +} + func (k *kubernetesAnnotator) addPod(pod *kubernetes.Pod) { metadata := k.indexers.GetMetadata(pod) for _, m := range metadata { diff --git a/libbeat/processors/add_process_metadata/add_process_metadata.go b/libbeat/processors/add_process_metadata/add_process_metadata.go index c41ca9a73d6..01e2cf1e9fe 100644 --- a/libbeat/processors/add_process_metadata/add_process_metadata.go +++ b/libbeat/processors/add_process_metadata/add_process_metadata.go @@ -55,11 +55,12 @@ var ( ) type addProcessMetadata struct { - config config - provider processMetadataProvider - cidProvider cidProvider - log *logp.Logger - mappings common.MapStr + config config + provider processMetadataProvider + cgroupsCache *common.Cache + cidProvider cidProvider + log *logp.Logger + mappings common.MapStr } type processMetadata struct { @@ -81,16 +82,22 @@ type cidProvider interface { } func init() { - processors.RegisterPlugin(processorName, New) + processors.RegisterPlugin(processorName, NewWithCache) jsprocessor.RegisterPlugin("AddProcessMetadata", New) } // New constructs a new add_process_metadata processor. func New(cfg *common.Config) (processors.Processor, error) { - return newProcessMetadataProcessorWithProvider(cfg, &procCache) + return newProcessMetadataProcessorWithProvider(cfg, &procCache, false) } -func newProcessMetadataProcessorWithProvider(cfg *common.Config, provider processMetadataProvider) (proc processors.Processor, err error) { +// NewWithCache construct a new add_process_metadata processor with cache for container IDs. +// Resulting processor implements `Close()` to release the cache resources. +func NewWithCache(cfg *common.Config) (processors.Processor, error) { + return newProcessMetadataProcessorWithProvider(cfg, &procCache, true) +} + +func newProcessMetadataProcessorWithProvider(cfg *common.Config, provider processMetadataProvider, withCache bool) (proc processors.Processor, err error) { // Logging (each processor instance has a unique ID). var ( id = int(instanceID.Inc()) @@ -118,21 +125,25 @@ func newProcessMetadataProcessorWithProvider(cfg *common.Config, provider proces } // don't use cgroup.ProcessCgroupPaths to save it from doing the work when container id disabled if ok := containsValue(mappings, "container.id"); ok { - if config.CgroupCacheExpireTime != 0 { + if withCache && config.CgroupCacheExpireTime != 0 { p.log.Debug("Initializing cgroup cache") evictionListener := func(k common.Key, v common.Value) { p.log.Debugf("Evicted cached cgroups for PID=%v", k) } - cgroupsCache := common.NewCacheWithRemovalListener(config.CgroupCacheExpireTime, 100, evictionListener) - cgroupsCache.StartJanitor(config.CgroupCacheExpireTime) - p.cidProvider = newCidProvider(config.HostPath, config.CgroupPrefixes, config.CgroupRegex, processCgroupPaths, cgroupsCache) + p.cgroupsCache = common.NewCacheWithRemovalListener(config.CgroupCacheExpireTime, 100, evictionListener) + p.cgroupsCache.StartJanitor(config.CgroupCacheExpireTime) + p.cidProvider = newCidProvider(config.HostPath, config.CgroupPrefixes, config.CgroupRegex, processCgroupPaths, p.cgroupsCache) } else { p.cidProvider = newCidProvider(config.HostPath, config.CgroupPrefixes, config.CgroupRegex, processCgroupPaths, nil) } } + if withCache { + return &addProcessMetadataCloser{p}, nil + } + return &p, nil } @@ -253,6 +264,17 @@ func (p *addProcessMetadata) getContainerID(pid int) (string, error) { return cid, nil } +type addProcessMetadataCloser struct { + addProcessMetadata +} + +func (p *addProcessMetadataCloser) Close() error { + if p.addProcessMetadata.cgroupsCache != nil { + p.addProcessMetadata.cgroupsCache.StopJanitor() + } + return nil +} + // String returns the processor representation formatted as a string func (p *addProcessMetadata) String() string { return fmt.Sprintf("%v=[match_pids=%v, mappings=%v, ignore_missing=%v, overwrite_fields=%v, restricted_fields=%v, host_path=%v, cgroup_prefixes=%v]", diff --git a/libbeat/processors/add_process_metadata/add_process_metadata_test.go b/libbeat/processors/add_process_metadata/add_process_metadata_test.go index f9b4aaa681c..493034098cf 100644 --- a/libbeat/processors/add_process_metadata/add_process_metadata_test.go +++ b/libbeat/processors/add_process_metadata/add_process_metadata_test.go @@ -651,7 +651,7 @@ func TestAddProcessMetadata(t *testing.T) { t.Fatal(err) } - proc, err := newProcessMetadataProcessorWithProvider(config, testProcs) + proc, err := newProcessMetadataProcessorWithProvider(config, testProcs, true) if test.initErr == nil { if err != nil { t.Fatal(err) diff --git a/libbeat/processors/processor.go b/libbeat/processors/processor.go index c5002b7cebd..0e772a524da 100644 --- a/libbeat/processors/processor.go +++ b/libbeat/processors/processor.go @@ -20,6 +20,7 @@ package processors import ( "strings" + "github.com/joeshaw/multierror" "github.com/pkg/errors" "github.com/elastic/beats/v7/libbeat/beat" @@ -35,11 +36,29 @@ type Processors struct { log *logp.Logger } +// Processor is the interface that all processors must implement type Processor interface { Run(event *beat.Event) (*beat.Event, error) String() string } +// Closer defines the interface for processors that should be closed after using +// them. +// Close() is not part of the Processor interface because implementing this method +// is also a way to indicate that the processor keeps some resource that needs to +// be released or orderly closed. +type Closer interface { + Close() error +} + +// Close closes a processor if it implements the Closer interface +func Close(p Processor) error { + if closer, ok := p.(Closer); ok { + return closer.Close() + } + return nil +} + // NewList creates a new empty processor list. // Additional processors can be added to the List field. func NewList(log *logp.Logger) *Processors { @@ -153,6 +172,17 @@ func (procs *Processors) All() []beat.Processor { return ret } +func (procs *Processors) Close() error { + var errs multierror.Errors + for _, p := range procs.List { + err := Close(p) + if err != nil { + errs = append(errs, err) + } + } + return errs.Err() +} + // Run executes the all processors serially and returns the event and possibly // an error. If the event has been dropped (canceled) by a processor in the // list then a nil event is returned. diff --git a/libbeat/processors/script/javascript/module/processor/chain.go b/libbeat/processors/script/javascript/module/processor/chain.go index e58aac29372..9ef3da7859e 100644 --- a/libbeat/processors/script/javascript/module/processor/chain.go +++ b/libbeat/processors/script/javascript/module/processor/chain.go @@ -151,6 +151,17 @@ func newNativeProcessor(constructor processors.Constructor, call gojaCall) (proc if err != nil { return nil, err } + + if closer, ok := p.(processors.Closer); ok { + closer.Close() + // Script processor doesn't support releasing resources of stateful processors, + // what can lead to leaks, so prevent use of these processors. They shouldn't + // be registered. If this error happens, a processor that needs to be closed is + // being registered, this should be avoided. + // See https://github.com/elastic/beats/pull/16349 + return nil, errors.Errorf("stateful processor cannot be used in script processor, this is probably a bug: %s", p) + } + return &nativeProcessor{p}, nil } diff --git a/libbeat/processors/script/javascript/module/processor/processor_test.go b/libbeat/processors/script/javascript/module/processor/processor_test.go index 6ea66f409ff..9e958ee7035 100644 --- a/libbeat/processors/script/javascript/module/processor/processor_test.go +++ b/libbeat/processors/script/javascript/module/processor/processor_test.go @@ -23,6 +23,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/elastic/beats/v7/libbeat/beat" "github.com/elastic/beats/v7/libbeat/common" @@ -35,6 +36,7 @@ import ( func init() { RegisterPlugin("Mock", newMock) + RegisterPlugin("MockWithCloser", newMockWithCloser) } func testEvent() *beat.Event { @@ -67,14 +69,10 @@ function process(evt) { logp.TestingSetup() p, err := javascript.NewFromConfig(javascript.Config{Source: script}, nil) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) evt, err := p.Run(testEvent()) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) checkEvent(t, evt, "added", "new_value") } @@ -107,14 +105,10 @@ function process(evt) { logp.TestingSetup() p, err := javascript.NewFromConfig(javascript.Config{Source: script}, nil) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) evt, err := p.Run(testEvent()) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) // checking if hello world is added to the event in different languages checkEvent(t, evt, "helló", "világ") @@ -123,6 +117,22 @@ function process(evt) { checkEvent(t, evt, "hallo", "Welt") } +func TestProcessorWithCloser(t *testing.T) { + const script = ` +var processor = require('processor'); + +var processorWithCloser = new processor.MockWithCloser().Build() + +function process(evt) { + processorWithCloser.Run(evt); +} +` + + logp.TestingSetup() + _, err := javascript.NewFromConfig(javascript.Config{Source: script}, nil) + require.Error(t, err, "processor that implements Closer() shouldn't be allowed") +} + func checkEvent(t *testing.T, evt *beat.Event, key, value string) { s, err := evt.GetValue(key) assert.NoError(t, err) @@ -162,3 +172,23 @@ func (m *mockProcessor) String() string { s, _ := json.Marshal(m.fields) return fmt.Sprintf("mock=%s", s) } + +type mockProcessorWithCloser struct{} + +func newMockWithCloser(c *common.Config) (processors.Processor, error) { + return &mockProcessorWithCloser{}, nil +} + +func (m *mockProcessorWithCloser) Run(event *beat.Event) (*beat.Event, error) { + // Nothing to do, we only want this struct to implement processors.Closer + return event, nil +} + +func (m *mockProcessorWithCloser) Close() error { + // Nothing to do, we only want this struct to implement processors.Closer + return nil +} + +func (m *mockProcessorWithCloser) String() string { + return "mockWithCloser" +} diff --git a/libbeat/publisher/pipeline/client.go b/libbeat/publisher/pipeline/client.go index 2ce792ed887..edb5a3f1eb3 100644 --- a/libbeat/publisher/pipeline/client.go +++ b/libbeat/publisher/pipeline/client.go @@ -24,6 +24,7 @@ import ( "github.com/elastic/beats/v7/libbeat/beat" "github.com/elastic/beats/v7/libbeat/common/atomic" "github.com/elastic/beats/v7/libbeat/logp" + "github.com/elastic/beats/v7/libbeat/processors" "github.com/elastic/beats/v7/libbeat/publisher" "github.com/elastic/beats/v7/libbeat/publisher/queue" ) @@ -164,6 +165,15 @@ func (c *client) Close() error { log.Debug("client: unlink from queue") c.unlink() log.Debug("client: done unlink") + + if c.processors != nil { + log.Debug("client: closing processors") + err := processors.Close(c.processors) + if err != nil { + log.Errorf("client: error closing processors: %v", err) + } + log.Debug("client: done closing processors") + } }) return nil } diff --git a/libbeat/publisher/processing/default.go b/libbeat/publisher/processing/default.go index b2a65642e17..f9eab88fe48 100644 --- a/libbeat/publisher/processing/default.go +++ b/libbeat/publisher/processing/default.go @@ -338,7 +338,10 @@ func (b *builder) Create(cfg beat.ProcessingConfig, drop bool) (beat.Processor, } // setup 8: pipeline processors list - processors.add(b.processors) + if b.processors != nil { + // Add the global pipeline as a function processor, so clients cannot close it + processors.add(newProcessor(b.processors.title, b.processors.Run)) + } // setup 9: time series metadata if b.timeSeries { @@ -358,6 +361,13 @@ func (b *builder) Create(cfg beat.ProcessingConfig, drop bool) (beat.Processor, return processors, nil } +func (b *builder) Close() error { + if b.processors != nil { + return b.processors.Close() + } + return nil +} + func makeClientProcessors( log *logp.Logger, cfg beat.ProcessingConfig, diff --git a/libbeat/publisher/processing/default_test.go b/libbeat/publisher/processing/default_test.go index 56dfa75bdd2..637b38cf44d 100644 --- a/libbeat/publisher/processing/default_test.go +++ b/libbeat/publisher/processing/default_test.go @@ -29,6 +29,7 @@ import ( "github.com/elastic/beats/v7/libbeat/beat" "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/logp" + "github.com/elastic/beats/v7/libbeat/processors" "github.com/elastic/beats/v7/libbeat/processors/actions" "github.com/elastic/ecs/code/go/ecs" ) @@ -317,6 +318,9 @@ func TestNormalization(t *testing.T) { fields.DeepUpdate(test.mod) assert.Equal(t, test.want, actual.Fields) + + err = s.Close() + require.NoError(t, err) }) } } @@ -331,6 +335,9 @@ func TestAlwaysDrop(t *testing.T) { actual, err := prog.Run(&beat.Event{}) require.NoError(t, err) assert.Nil(t, actual) + + err = s.Close() + require.NoError(t, err) } func TestDynamicFields(t *testing.T) { @@ -351,6 +358,52 @@ func TestDynamicFields(t *testing.T) { actual, err = prog.Run(&beat.Event{Fields: common.MapStr{"hello": "world"}}) require.NoError(t, err) assert.Equal(t, common.MapStr{"hello": "world", "dyn": "field"}, actual.Fields) + + err = factory.Close() + require.NoError(t, err) +} + +func TestProcessingClose(t *testing.T) { + factory, err := MakeDefaultSupport(true)(beat.Info{}, logp.L(), common.NewConfig()) + require.NoError(t, err) + + // Inject a processor in the builder that we can check if has been closed. + factoryProcessor := &processorWithClose{} + b := factory.(*builder) + if b.processors == nil { + b.processors = newGroup("global", logp.L()) + } + b.processors.add(factoryProcessor) + + clientProcessor := &processorWithClose{} + g := newGroup("test", logp.L()) + g.add(clientProcessor) + + prog, err := factory.Create(beat.ProcessingConfig{ + Processor: g, + }, false) + require.NoError(t, err) + + // Check that both processors are called + assert.False(t, factoryProcessor.called) + assert.False(t, clientProcessor.called) + _, err = prog.Run(&beat.Event{Fields: common.MapStr{"hello": "world"}}) + require.NoError(t, err) + assert.True(t, factoryProcessor.called) + assert.True(t, clientProcessor.called) + + // Check that closing the client processing pipeline doesn't close the global pipeline + assert.False(t, factoryProcessor.closed) + assert.False(t, clientProcessor.closed) + err = processors.Close(prog) + require.NoError(t, err) + assert.False(t, factoryProcessor.closed) + assert.True(t, clientProcessor.closed) + + // Check that closing the factory closes the processor in the global pipeline + err = factory.Close() + require.NoError(t, err) + assert.True(t, factoryProcessor.closed) } func fromJSON(in string) common.MapStr { @@ -361,3 +414,22 @@ func fromJSON(in string) common.MapStr { } return tmp } + +type processorWithClose struct { + closed bool + called bool +} + +func (p *processorWithClose) Run(e *beat.Event) (*beat.Event, error) { + p.called = true + return e, nil +} + +func (p *processorWithClose) Close() error { + p.closed = true + return nil +} + +func (p *processorWithClose) String() string { + return "processorWithClose" +} diff --git a/libbeat/publisher/processing/processing.go b/libbeat/publisher/processing/processing.go index 4f615fb1422..88feb62c7b2 100644 --- a/libbeat/publisher/processing/processing.go +++ b/libbeat/publisher/processing/processing.go @@ -33,6 +33,8 @@ type SupportFactory func(info beat.Info, log *logp.Logger, cfg *common.Config) ( // will merge the global and local configurations into a common event // processor. // If `drop` is set, then the processor generated must always drop all events. +// A Supporter needs to be closed with `Close()` to release its global resources. type Supporter interface { Create(cfg beat.ProcessingConfig, drop bool) (beat.Processor, error) + Close() error } diff --git a/libbeat/publisher/processing/processors.go b/libbeat/publisher/processing/processors.go index e994eef48cc..3a400d36dad 100644 --- a/libbeat/publisher/processing/processors.go +++ b/libbeat/publisher/processing/processors.go @@ -22,6 +22,8 @@ import ( "strings" "sync" + "github.com/joeshaw/multierror" + "github.com/elastic/beats/v7/libbeat/beat" "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/logp" @@ -77,6 +79,20 @@ func (p *group) add(processor processors.Processor) { } } +func (p *group) Close() error { + if p == nil { + return nil + } + var errs multierror.Errors + for _, processor := range p.list { + err := processors.Close(processor) + if err != nil { + errs = append(errs, err) + } + } + return errs.Err() +} + func (p *group) String() string { var s []string for _, p := range p.list { diff --git a/libbeat/tests/docker/docker.go b/libbeat/tests/docker/docker.go index b81ffa285ea..888347c5cc7 100644 --- a/libbeat/tests/docker/docker.go +++ b/libbeat/tests/docker/docker.go @@ -77,8 +77,28 @@ func (c Client) ContainerWait(ID string) error { return nil } +// ContainerInspect recovers information of the container +func (c Client) ContainerInspect(ID string) (types.ContainerJSON, error) { + ctx := context.Background() + return c.cli.ContainerInspect(ctx, ID) +} + // ContainerKill kills the given container func (c Client) ContainerKill(ID string) error { ctx := context.Background() return c.cli.ContainerKill(ctx, ID, "KILL") } + +// ContainerRemove kills and removed the given container +func (c Client) ContainerRemove(ID string) error { + ctx := context.Background() + return c.cli.ContainerRemove(ctx, ID, types.ContainerRemoveOptions{ + RemoveVolumes: true, + Force: true, + }) +} + +// Close closes the underlying client +func (c *Client) Close() error { + return c.cli.Close() +} From 500e8b5f81af3a9d0cd369182ac879360b39c64b Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Tue, 13 Oct 2020 07:14:59 -0400 Subject: [PATCH 75/93] Remove dot from file.extension value in Auditbeat FIM (#21644) The ECS file.extension field should not include the dot. For example the value should be "png" and not ".png". Relates elastic/ecs#1016 --- CHANGELOG.next.asciidoc | 1 + auditbeat/module/file_integrity/event.go | 2 +- auditbeat/module/file_integrity/event_test.go | 11 +++++++++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 576c0062310..660fa25a10d 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -32,6 +32,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Change event.kind=error to event.kind=event to comply with ECS. {issue}18870[18870] {pull}20685[20685] - Change network.direction values to ECS recommended values (inbound, outbound). {issue}12445[12445] {pull}20695[20695] - Docker container needs to be explicitly run as user root for auditing. {pull}21202[21202] +- File integrity dataset no longer includes the leading dot in `file.extension` values (e.g. it will report "png" instead of ".png") to comply with ECS. {pull}21644[21644] *Filebeat* diff --git a/auditbeat/module/file_integrity/event.go b/auditbeat/module/file_integrity/event.go index 41e4d5a3795..1ee28b7ce35 100644 --- a/auditbeat/module/file_integrity/event.go +++ b/auditbeat/module/file_integrity/event.go @@ -257,7 +257,7 @@ func buildMetricbeatEvent(e *Event, existedBefore bool) mb.Event { if e.Info.Type == FileType { if extension := filepath.Ext(e.Path); extension != "" { - file["extension"] = extension + file["extension"] = strings.TrimLeft(extension, ".") } if mimeType := getMimeType(e.Path); mimeType != "" { file["mime_type"] = mimeType diff --git a/auditbeat/module/file_integrity/event_test.go b/auditbeat/module/file_integrity/event_test.go index 79d1309903d..efaafd02041 100644 --- a/auditbeat/module/file_integrity/event_test.go +++ b/auditbeat/module/file_integrity/event_test.go @@ -28,6 +28,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/elastic/beats/v7/libbeat/common" ) @@ -295,7 +296,11 @@ func TestBuildEvent(t *testing.T) { assertHasKey(t, fields, "event.type") assertHasKey(t, fields, "file.path") - assertHasKey(t, fields, "file.extension") + if assertHasKey(t, fields, "file.extension") { + ext, err := fields.GetValue("file.extension") + require.NoError(t, err) + assert.Equal(t, ext, "txt") + } assertHasKey(t, fields, "file.target_path") assertHasKey(t, fields, "file.inode") assertHasKey(t, fields, "file.uid") @@ -427,10 +432,12 @@ func mustDecodeHex(v string) []byte { return data } -func assertHasKey(t testing.TB, m common.MapStr, key string) { +func assertHasKey(t testing.TB, m common.MapStr, key string) bool { t.Helper() found, err := m.HasKey(key) if err != nil || !found { t.Errorf("key %v not found: %v", key, err) + return false } + return true } From 09234d4e94d4813892160f7d313bc6dec66ca037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Tue, 13 Oct 2020 13:17:06 +0200 Subject: [PATCH 76/93] fix: update fleet test suite name (#21738) --- .ci/packaging.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/packaging.groovy b/.ci/packaging.groovy index c65fd7f8b56..9087cf48de4 100644 --- a/.ci/packaging.groovy +++ b/.ci/packaging.groovy @@ -230,11 +230,11 @@ def runE2ETestForPackages(){ catchError(buildResult: 'UNSTABLE', message: 'Unable to run e2e tests', stageResult: 'FAILURE') { if ("${env.BEATS_FOLDER}" == "filebeat" || "${env.BEATS_FOLDER}" == "x-pack/filebeat") { - suite = 'helm,ingest-manager' + suite = 'helm,fleet' } else if ("${env.BEATS_FOLDER}" == "metricbeat" || "${env.BEATS_FOLDER}" == "x-pack/metricbeat") { suite = '' } else if ("${env.BEATS_FOLDER}" == "x-pack/elastic-agent") { - suite = 'ingest-manager' + suite = 'fleet' } else { echo("Skipping E2E tests for ${env.BEATS_FOLDER}.") return From 1824e849978f248f4e2da75ae51732963379714a Mon Sep 17 00:00:00 2001 From: EamonnTP Date: Tue, 13 Oct 2020 13:49:48 +0100 Subject: [PATCH 77/93] Update obs app links (#21682) --- libbeat/docs/shared/obs-apps.asciidoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libbeat/docs/shared/obs-apps.asciidoc b/libbeat/docs/shared/obs-apps.asciidoc index 9b5f7354ea0..26698700c21 100644 --- a/libbeat/docs/shared/obs-apps.asciidoc +++ b/libbeat/docs/shared/obs-apps.asciidoc @@ -38,13 +38,13 @@ endif::[] |=== |Elastic apps | Use to -|{kibana-ref}/xpack-infra.html[{metrics-app}] +|{observability-guide}/analyze-metrics.html[{metrics-app}] |Explore metrics about systems and services across your ecosystem -|{kibana-ref}/xpack-logs.html[{logs-app}] +|{observability-guide}/monitor-logs.html[{logs-app}] |Tail related log data in real time -|{kibana-ref}/xpack-uptime.html[{uptime-app}] +|{observability-guide}/monitor-uptime.html[{uptime-app}] |Monitor availability issues across your apps and services |{kibana-ref}/xpack-apm.html[APM app] From 056f0e07b233d3a438b1d21b5182cb2ea49638d2 Mon Sep 17 00:00:00 2001 From: Mariana Dima Date: Tue, 13 Oct 2020 16:01:19 +0200 Subject: [PATCH 78/93] Fix for azure retrieve resource by ids (#21711) * mofidy doc * start work * work * changelog * fix test --- CHANGELOG.next.asciidoc | 1 + x-pack/metricbeat/module/azure/client.go | 6 ++--- x-pack/metricbeat/module/azure/client_test.go | 2 +- .../metricbeat/module/azure/mock_service.go | 4 +-- .../module/azure/monitor_service.go | 26 ++++++++++++++----- .../module/azure/service_interface.go | 2 +- 6 files changed, 27 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 660fa25a10d..41de16190a8 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -366,6 +366,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Fix remote_write flaky test. {pull}21173[21173] - Visualization title fixes in aws, azure and googlecloud compute dashboards. {pull}21098[21098] - Add a switch to the driver definition on SQL module to use pretty names {pull}17378[17378] +- Fix retrieving resources by ID for the azure module. {pull}21711[21711] {issue}21707[21707] - Use timestamp from CloudWatch API when creating events. {pull}21498[21498] *Packetbeat* diff --git a/x-pack/metricbeat/module/azure/client.go b/x-pack/metricbeat/module/azure/client.go index e488fab98b6..dd48f962b59 100644 --- a/x-pack/metricbeat/module/azure/client.go +++ b/x-pack/metricbeat/module/azure/client.go @@ -65,14 +65,14 @@ func (client *Client) InitResources(fn mapResourceMetrics) error { err = errors.Wrap(err, "failed to retrieve resources") return err } - if len(resourceList.Values()) == 0 { + if len(resourceList) == 0 { err = errors.Errorf("failed to retrieve resources: No resources returned using the configuration options resource ID %s, resource group %s, resource type %s, resource query %s", resource.Id, resource.Group, resource.Type, resource.Query) client.Log.Error(err) continue } //map resources to the client - for _, resource := range resourceList.Values() { + for _, resource := range resourceList { if !containsResource(*resource.ID, client.Resources) { client.Resources = append(client.Resources, Resource{ Id: *resource.ID, @@ -84,7 +84,7 @@ func (client *Client) InitResources(fn mapResourceMetrics) error { Subscription: client.Config.SubscriptionId}) } } - resourceMetrics, err := fn(client, resourceList.Values(), resource) + resourceMetrics, err := fn(client, resourceList, resource) if err != nil { return err } diff --git a/x-pack/metricbeat/module/azure/client_test.go b/x-pack/metricbeat/module/azure/client_test.go index 47b88f99cce..6b0df97a370 100644 --- a/x-pack/metricbeat/module/azure/client_test.go +++ b/x-pack/metricbeat/module/azure/client_test.go @@ -50,7 +50,7 @@ func TestInitResources(t *testing.T) { client := NewMockClient() client.Config = resourceQueryConfig m := &MockService{} - m.On("GetResourceDefinitions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(resources.ListResultPage{}, errors.New("invalid resource query")) + m.On("GetResourceDefinitions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]resources.GenericResource{}, errors.New("invalid resource query")) client.AzureMonitorService = m mr := MockReporterV2{} mr.On("Error", mock.Anything).Return(true) diff --git a/x-pack/metricbeat/module/azure/mock_service.go b/x-pack/metricbeat/module/azure/mock_service.go index f6f54c300e0..601f1de5b45 100644 --- a/x-pack/metricbeat/module/azure/mock_service.go +++ b/x-pack/metricbeat/module/azure/mock_service.go @@ -24,9 +24,9 @@ func (client *MockService) GetResourceDefinitionById(id string) (resources.Gener } // GetResourceDefinitions is a mock function for the azure service -func (client *MockService) GetResourceDefinitions(id []string, group []string, rType string, query string) (resources.ListResultPage, error) { +func (client *MockService) GetResourceDefinitions(id []string, group []string, rType string, query string) ([]resources.GenericResource, error) { args := client.Called(id, group, rType, query) - return args.Get(0).(resources.ListResultPage), args.Error(1) + return args.Get(0).([]resources.GenericResource), args.Error(1) } // GetMetricDefinitions is a mock function for the azure service diff --git a/x-pack/metricbeat/module/azure/monitor_service.go b/x-pack/metricbeat/module/azure/monitor_service.go index 053da3db05b..c3ed4e2fa43 100644 --- a/x-pack/metricbeat/module/azure/monitor_service.go +++ b/x-pack/metricbeat/module/azure/monitor_service.go @@ -55,16 +55,24 @@ func NewService(clientId string, clientSecret string, tenantId string, subscript } // GetResourceDefinitions will retrieve the azure resources based on the options entered -func (service MonitorService) GetResourceDefinitions(id []string, group []string, rType string, query string) (resources.ListResultPage, error) { +func (service MonitorService) GetResourceDefinitions(id []string, group []string, rType string, query string) ([]resources.GenericResource, error) { var resourceQuery string + var resourceList []resources.GenericResource if len(id) > 0 { - var filterList []string - // listing resourceID conditions does not seem to work with the API but querying by name or resource types will work + // listing multiple resourceId conditions does not seem to work with the API, extracting the name and resource type does not work as the position of the `resourceType` can move if a parent resource is involved, filtering by resource name and resource group (if extracted) is also not possible as + // different types of resources can contain the same name. for _, id := range id { - filterList = append(filterList, fmt.Sprintf("name eq '%s'", getResourceNameFromId(id))) + resource, err := service.resourceClient.List(service.context, fmt.Sprintf("resourceId eq '%s'", id), "", nil) + if err != nil { + return nil, err + } + if len(resource.Values()) > 0 { + resourceList = append(resourceList, resource.Values()...) + } } - resourceQuery = fmt.Sprintf("(%s) AND resourceType eq '%s'", strings.Join(filterList, " OR "), getResourceTypeFromId(id[0])) - } else if len(group) > 0 { + return resourceList, nil + } + if len(group) > 0 { var filterList []string for _, gr := range group { filterList = append(filterList, fmt.Sprintf("resourceGroup eq '%s'", gr)) @@ -76,7 +84,11 @@ func (service MonitorService) GetResourceDefinitions(id []string, group []string } else if query != "" { resourceQuery = query } - return service.resourceClient.List(service.context, resourceQuery, "", nil) + result, err := service.resourceClient.List(service.context, resourceQuery, "", nil) + if err == nil { + resourceList = result.Values() + } + return resourceList, err } // GetResourceDefinitionById will retrieve the azure resource based on the resource Id diff --git a/x-pack/metricbeat/module/azure/service_interface.go b/x-pack/metricbeat/module/azure/service_interface.go index e8985a7eedd..30430d03100 100644 --- a/x-pack/metricbeat/module/azure/service_interface.go +++ b/x-pack/metricbeat/module/azure/service_interface.go @@ -12,7 +12,7 @@ import ( // Service interface for the azure monitor service and mock for testing type Service interface { GetResourceDefinitionById(id string) (resources.GenericResource, error) - GetResourceDefinitions(id []string, group []string, rType string, query string) (resources.ListResultPage, error) + GetResourceDefinitions(id []string, group []string, rType string, query string) ([]resources.GenericResource, error) GetMetricDefinitions(resourceId string, namespace string) (insights.MetricDefinitionCollection, error) GetMetricNamespaces(resourceId string) (insights.MetricNamespaceCollection, error) GetMetricValues(resourceId string, namespace string, timegrain string, timespan string, metricNames []string, aggregations string, filter string) ([]insights.Metric, string, error) From 32d45264d27acb5c62a272431dff96c47d1dea83 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Tue, 13 Oct 2020 16:36:02 +0200 Subject: [PATCH 79/93] Skip flaky test with oauth2 config in httpjson input (#21749) --- x-pack/filebeat/input/httpjson/config_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/filebeat/input/httpjson/config_test.go b/x-pack/filebeat/input/httpjson/config_test.go index 85c7c64848d..1986ee7abe9 100644 --- a/x-pack/filebeat/input/httpjson/config_test.go +++ b/x-pack/filebeat/input/httpjson/config_test.go @@ -362,6 +362,7 @@ func TestConfigOauth2Validation(t *testing.T) { "url": "localhost", }, }, + /* Flaky test: https://github.com/elastic/beats/issues/21748 { name: "date_cursor.date_format will fail if invalid", expectedErr: "invalid configuration: date_format is not a valid date layout accessing 'date_cursor'", @@ -370,6 +371,7 @@ func TestConfigOauth2Validation(t *testing.T) { "url": "localhost", }, }, + */ { name: "date_cursor must work with a valid date_format", input: map[string]interface{}{ From f754515e287672405bb1f1b6613deb17471d1f70 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Tue, 13 Oct 2020 18:55:46 +0200 Subject: [PATCH 80/93] Remove kafka partition ISR from Metricbeat docs (#21709) Metricbeat doesn't collect this field directly, it collects for each replica if it is in sync or not. --- metricbeat/docs/fields.asciidoc | 10 ---------- metricbeat/module/kafka/fields.go | 2 +- metricbeat/module/kafka/partition/_meta/fields.yml | 4 ---- 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/metricbeat/docs/fields.asciidoc b/metricbeat/docs/fields.asciidoc index 7f1d0f69ab9..a193d630564 100644 --- a/metricbeat/docs/fields.asciidoc +++ b/metricbeat/docs/fields.asciidoc @@ -24870,16 +24870,6 @@ type: long -- -*`kafka.partition.partition.isr`*:: -+ --- -List of isr ids. - - -type: keyword - --- - *`kafka.partition.partition.replica`*:: + -- diff --git a/metricbeat/module/kafka/fields.go b/metricbeat/module/kafka/fields.go index 72ee2cdc60c..96c306b5915 100644 --- a/metricbeat/module/kafka/fields.go +++ b/metricbeat/module/kafka/fields.go @@ -32,5 +32,5 @@ func init() { // AssetKafka returns asset data. // This is the base64 encoded gzipped contents of module/kafka. func AssetKafka() string { - return "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" + return "eJzUWk9v3LYSv/tTDHJyDlFO7x18eMBrUhRumiZIU6DoReCSo13WEqmQ1NqbT1+QlLT6S1Grddr45JU08/txhhwOZ/gKHvB0Bw8keyA3AIabHO/gxTv7+8UNAENNFS8Nl+IO/ncDAODeQSFZleMNgD5IZVIqRcb3d5CRXNunCnMkGu9gb9VmHHOm75z4KxCkwDOk/TOn0n6qZFXWTyZw+2q6qnZKPqBqH0/pm9Xp/35wGuCNFLoqUMFPVhTuRSZVQawAHMgRYYcoQCFhkClZwG0tdiCC5VzseyrNAYE2+hyVl0nng+FYuuPhrPe4GU8uBxDBIXWGxdnNJA5hTKHWk2APeHqUakgkDo+wIyrDNbIWYuQzI0tOE/v/yG9j6ADsZ6vH6ZzDQKWkSqhkY6SBRRdhnCqwqpIxWkmU4VY26flvLdLHRg1wFkRxo0snsML264H9LviXCoEzkJmbseUZXbgH3obLPPwa/DZ0gAjmfnnQ5CoBoZ67BRrFqfYL3Ie6+s3P7//oyLYBboeGRK7rYodEhFbUe/sBmAMxYA5cAx5RGODaohGDDIyMXqwNqMIvFWqT0AMRAvPkS4UVJpp/xRCTzwcE+03jiFoLOOm46DQkUCrJKopJRniOLC1RpRqpFMEYY3koYhwPLwi1nkavhhIVTGryxLJcEhNklqGhh8t50ZxbNzktraGstkrhFdj17bZESlTFDlXAXFewUTyHoGlWMylzTt1unORIGKoUc6T291DViJH/Hprvnes2wFeC5khEupZGLXcNOhq1tlS+SvmAWKJKGNdUCoHULNH4U8p3TgZoLu0uXSvbMFnHdPCp5Goxxpyp+O+fh4tN2aTIT/FsGolnoaNPgq7xkVtDtW+3ccnlPsnySh/SiSk3XjVyD+7rSyZoneChSbhIdieDugmtS7BcUFlwsQcr5aOsHbBTeDEJWZl1LGRl9vLaLBT+hdQgW0elkboalQK1JnvUKQ+mIz1n1DLb4K8zHS4AvYL7L0C9lrtXQm91bwRcA9WccNfl2u05eyLbbt99p/m2y3WiwmvBBS+qwq8oYuDxwOmhXzfQKJjup08ajAQyPuLETAw/DWvti2kcOaKyU+Kczjn5hh2DTCogoEukPOO0PpttyHepVGwLvVrDmeCZyyTXlQTXBq7mfNBYza0ze46VPSevXdzkKc1JsBDkJhd5cpOrnUpjmSWkNmFJqSwKPjo6zA5YZplGm7E4KTveNptZScEVCbfDv+vUGqPtHB9E24PgOaz5YOofuC8viKnDELq6qjmnqF+cbf5CkTQQbYbKPadSIbUR9A7+m/wnZL/ZIuI1a7GwXI+dswCE6rIQqs1GDBV6NdrmySz+dL0WFmq263gMMaYj4XRsj6gZD736Ngw0H6bnsKInXqdwHKTQliDjql6rOJxrvUuG8DEtnkIHpJ30dVzkwshOYXWHdluyCynMoOjnXbDG67TSRhbd+GgIMGIIaKO6i3USuRGb2PZWWiAne5cItKN/7XMWSnJa+YyPaBcpGM8yVCioDTTm0caafj26NiYRbGjg8GAmuxHxQxmvXbdFthxenxl2mxVh+7oCw4rgHuTzf635XiBr6hZ2ZtkZ5nLrOtOfW1MbAnBk7HvjSd2/hVtvOI3GWHqebcLZy+U4fJB6aK5LifRUzQIWaFPcdPvwuTCoBMkHO2EN0I1CofC3OhGZUrI+CQnEwEvm6ZHwnOxyrPXqptWx50cUnf7Wyjkq8BED0+PyROFXp7gJPMMG2ZBmx2w5ex5CH5ziZUIXbKsX+PO8l9qN5VukdqFtPoIwjJq9zdNZlr5v8Ayu/MU3JDiDW5/qj3LjUSPkGVh88pqnacy7TuiToOkSrZ2U+bgcFMnsXjBufa2BZ40BgGvgguYVQ9a0p7l4Zcm0zSK0+wvc3v/2KWokOl3w8LMMwrQNsmWKs+kLXMH/P7YZi08TXN3Lbs6xQSVw4SPIb9N51YyuhsxTG9x3gesdZ8a8Yg849SWKNZeOtp/u+XTxtOZyycWk7Zxmj77jpMj31FdWvT/WUlNV7/bdd1r1Jk02le4qe3JKXdEzeP52t3kMyYEUshIugfCyNhuVatisXaxtE0MPqeZfMSXHxfLoXG1bz52EooAL8rQE3NRlo4ED92qoVCzVKFhUu2G+Xm6xt9btU4VGnS4mYhRHVququx5bCbmI/C8i5PsGdRfnH3bW2mXS2GF8a2wN4IrlsQS4dNssyu9n4zYB/SqXynQphcbLGXj5DRS4TB8JX5xjLeT96w9gBcDwmfRkHmt1Y73fFfM9dlkZV5czHVYredQtlyirl+eNOKrp/XcAAAD//1uFyto=" } diff --git a/metricbeat/module/kafka/partition/_meta/fields.yml b/metricbeat/module/kafka/partition/_meta/fields.yml index 8f241278589..cf40ad266b9 100644 --- a/metricbeat/module/kafka/partition/_meta/fields.yml +++ b/metricbeat/module/kafka/partition/_meta/fields.yml @@ -33,10 +33,6 @@ type: long description: > Leader id (broker). - - name: isr - type: keyword - description: > - List of isr ids. - name: replica type: long description: > From 7addb4d45502d6f1bc0d5ea9823dc5a926cda9ea Mon Sep 17 00:00:00 2001 From: kaiyan-sheng Date: Tue, 13 Oct 2020 11:36:12 -0600 Subject: [PATCH 81/93] [Filebeat] Add check for context.DeadlineExceeded error (#21732) --- x-pack/filebeat/input/s3/collector.go | 34 ++++++++++++--------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/x-pack/filebeat/input/s3/collector.go b/x-pack/filebeat/input/s3/collector.go index bf294f94245..1b890513284 100644 --- a/x-pack/filebeat/input/s3/collector.go +++ b/x-pack/filebeat/input/s3/collector.go @@ -148,8 +148,7 @@ func (c *s3Collector) processMessage(svcS3 s3iface.ClientAPI, message sqs.Messag // read from s3 object and create event for each log line err = c.handleS3Objects(svcS3, s3Infos, errC) if err != nil { - err = fmt.Errorf("handleS3Objects failed: %w", err) - c.logger.Error(err) + c.logger.Error(fmt.Errorf("handleS3Objects failed: %w", err)) return err } c.logger.Debugf("handleS3Objects succeed") @@ -163,7 +162,12 @@ func (c *s3Collector) processorKeepAlive(svcSQS sqsiface.ClientAPI, message sqs. return nil case err := <-errC: if err != nil { - c.logger.Warn("Processing message failed, updating visibility timeout") + if err == context.DeadlineExceeded { + c.logger.Info("Context deadline exceeded, updating visibility timeout") + } else { + c.logger.Warnf("Processing message failed '%w', updating visibility timeout", err) + } + err := c.changeVisibilityTimeout(queueURL, visibilityTimeout, svcSQS, message.ReceiptHandle) if err != nil { c.logger.Error(fmt.Errorf("SQS ChangeMessageVisibilityRequest failed: %w", err)) @@ -298,8 +302,7 @@ func (c *s3Collector) handleS3Objects(svc s3iface.ClientAPI, s3Infos []s3Info, e c.logger.Debugf("Processing file from s3 bucket \"%s\" with name \"%s\"", info.name, info.key) err := c.createEventsFromS3Info(svc, info, s3Ctx) if err != nil { - err = fmt.Errorf("createEventsFromS3Info failed processing file from s3 bucket \"%s\" with name \"%s\": %w", info.name, info.key, err) - c.logger.Error(err) + c.logger.Error(fmt.Errorf("createEventsFromS3Info failed processing file from s3 bucket \"%s\" with name \"%s\": %w", info.name, info.key, err)) s3Ctx.setError(err) } } @@ -326,8 +329,7 @@ func (c *s3Collector) createEventsFromS3Info(svc s3iface.ClientAPI, info s3Info, // If the SDK can determine the request or retry delay was canceled // by a context the ErrCodeRequestCanceled error will be returned. if awsErr.Code() == awssdk.ErrCodeRequestCanceled { - err = fmt.Errorf("s3 GetObjectRequest canceled for '%s' from S3 bucket '%s': %w", info.key, info.name, err) - c.logger.Error(err) + c.logger.Error(fmt.Errorf("s3 GetObjectRequest canceled for '%s' from S3 bucket '%s': %w", info.key, info.name, err)) return err } @@ -345,16 +347,14 @@ func (c *s3Collector) createEventsFromS3Info(svc s3iface.ClientAPI, info s3Info, isS3ObjGzipped, err := isStreamGzipped(reader) if err != nil { - err = fmt.Errorf("could not determine if S3 object is gzipped: %w", err) - c.logger.Error(err) + c.logger.Error(fmt.Errorf("could not determine if S3 object is gzipped: %w", err)) return err } if isS3ObjGzipped { gzipReader, err := gzip.NewReader(reader) if err != nil { - err = fmt.Errorf("gzip.NewReader failed for '%s' from S3 bucket '%s': %w", info.key, info.name, err) - c.logger.Error(err) + c.logger.Error(fmt.Errorf("gzip.NewReader failed for '%s' from S3 bucket '%s': %w", info.key, info.name, err)) return err } reader = bufio.NewReader(gzipReader) @@ -366,8 +366,7 @@ func (c *s3Collector) createEventsFromS3Info(svc s3iface.ClientAPI, info s3Info, decoder := json.NewDecoder(reader) err := c.decodeJSON(decoder, objectHash, info, s3Ctx) if err != nil { - err = fmt.Errorf("decodeJSONWithKey failed for '%s' from S3 bucket '%s': %w", info.key, info.name, err) - c.logger.Error(err) + c.logger.Error(fmt.Errorf("decodeJSONWithKey failed for '%s' from S3 bucket '%s': %w", info.key, info.name, err)) return err } return nil @@ -383,14 +382,12 @@ func (c *s3Collector) createEventsFromS3Info(svc s3iface.ClientAPI, info s3Info, event := createEvent(log, offset, info, objectHash, s3Ctx) err = c.forwardEvent(event) if err != nil { - err = fmt.Errorf("forwardEvent failed: %w", err) - c.logger.Error(err) + c.logger.Error(fmt.Errorf("forwardEvent failed: %w", err)) return err } return nil } else if err != nil { - err = fmt.Errorf("readStringAndTrimDelimiter failed: %w", err) - c.logger.Error(err) + c.logger.Error(fmt.Errorf("readStringAndTrimDelimiter failed: %w", err)) return err } @@ -403,8 +400,7 @@ func (c *s3Collector) createEventsFromS3Info(svc s3iface.ClientAPI, info s3Info, event := createEvent(log, offset, info, objectHash, s3Ctx) err = c.forwardEvent(event) if err != nil { - err = fmt.Errorf("forwardEvent failed: %w", err) - c.logger.Error(err) + c.logger.Error(fmt.Errorf("forwardEvent failed: %w", err)) return err } } From b0fbfaed277c6c04c23a4be8d30c2a512d915084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9mi=20V=C3=A1nyi?= Date: Tue, 13 Oct 2020 20:44:32 +0200 Subject: [PATCH 82/93] Add missing configuration annotations (#21736) --- filebeat/input/filestream/config.go | 2 +- filebeat/input/filestream/fswatch.go | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/filebeat/input/filestream/config.go b/filebeat/input/filestream/config.go index c2b1e838ee5..5b582ccf6e8 100644 --- a/filebeat/input/filestream/config.go +++ b/filebeat/input/filestream/config.go @@ -34,7 +34,7 @@ type config struct { Paths []string `config:"paths"` Close closerConfig `config:"close"` - FileWatcher *common.ConfigNamespace `config:"file_watcher"` + FileWatcher *common.ConfigNamespace `config:"prospector"` FileIdentity *common.ConfigNamespace `config:"file_identity"` CleanInactive time.Duration `config:"clean_inactive" validate:"min=0"` CleanRemoved bool `config:"clean_removed"` diff --git a/filebeat/input/filestream/fswatch.go b/filebeat/input/filestream/fswatch.go index 1b80971d835..e988fb3cee9 100644 --- a/filebeat/input/filestream/fswatch.go +++ b/filebeat/input/filestream/fswatch.go @@ -57,9 +57,9 @@ type fileScanner struct { type fileWatcherConfig struct { // Interval is the time between two scans. - Interval time.Duration + Interval time.Duration `config:"check_interval"` // Scanner is the configuration of the scanner. - Scanner fileScannerConfig + Scanner fileScannerConfig `config:",inline"` } // fileWatcher gets the list of files from a FSWatcher and creates events by @@ -212,10 +212,9 @@ func (w *fileWatcher) Event() loginp.FSEvent { } type fileScannerConfig struct { - Paths []string - ExcludedFiles []match.Matcher - Symlinks bool - RecursiveGlob bool + ExcludedFiles []match.Matcher `config:"exclude_files"` + Symlinks bool `config:"symlinks"` + RecursiveGlob bool `config:"recursive_glob"` } func defaultFileScannerConfig() fileScannerConfig { From fff5f6a2241e4c9c9513c98c7e821f0a5007d362 Mon Sep 17 00:00:00 2001 From: Michal Pristas Date: Tue, 13 Oct 2020 22:35:02 +0200 Subject: [PATCH 83/93] [Ingest Manager] Agent atomic installer (#21745) [Ingest Manager] Agent atomic installer (#21745) --- .../install/atomic/atomic_installer.go | 62 ++++++++++ .../install/atomic/atomic_installer_test.go | 115 ++++++++++++++++++ .../pkg/artifact/install/installer.go | 8 +- 3 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 x-pack/elastic-agent/pkg/artifact/install/atomic/atomic_installer.go create mode 100644 x-pack/elastic-agent/pkg/artifact/install/atomic/atomic_installer_test.go diff --git a/x-pack/elastic-agent/pkg/artifact/install/atomic/atomic_installer.go b/x-pack/elastic-agent/pkg/artifact/install/atomic/atomic_installer.go new file mode 100644 index 00000000000..5e26436bfc4 --- /dev/null +++ b/x-pack/elastic-agent/pkg/artifact/install/atomic/atomic_installer.go @@ -0,0 +1,62 @@ +// 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 atomic + +import ( + "context" + "io/ioutil" + "os" + "path/filepath" +) + +type embeddedInstaller interface { + Install(ctx context.Context, programName, version, installDir string) error +} + +// Installer installs into temporary destination and moves to correct one after +// successful finish. +type Installer struct { + installer embeddedInstaller +} + +// NewInstaller creates a new AtomicInstaller +func NewInstaller(i embeddedInstaller) (*Installer, error) { + return &Installer{ + installer: i, + }, nil +} + +// Install performs installation of program in a specific version. +func (i *Installer) Install(ctx context.Context, programName, version, installDir string) error { + // tar installer uses Dir of installDir to determine location of unpack + tempDir, err := ioutil.TempDir(os.TempDir(), "elastic-agent-install") + if err != nil { + return err + } + tempInstallDir := filepath.Join(tempDir, filepath.Base(installDir)) + + // cleanup install directory before Install + if _, err := os.Stat(installDir); err == nil || os.IsExist(err) { + os.RemoveAll(installDir) + } + + if _, err := os.Stat(tempInstallDir); err == nil || os.IsExist(err) { + os.RemoveAll(tempInstallDir) + } + + if err := i.installer.Install(ctx, programName, version, tempInstallDir); err != nil { + // cleanup unfinished install + os.RemoveAll(tempInstallDir) + return err + } + + if err := os.Rename(tempInstallDir, installDir); err != nil { + os.RemoveAll(installDir) + os.RemoveAll(tempInstallDir) + return err + } + + return nil +} diff --git a/x-pack/elastic-agent/pkg/artifact/install/atomic/atomic_installer_test.go b/x-pack/elastic-agent/pkg/artifact/install/atomic/atomic_installer_test.go new file mode 100644 index 00000000000..d6266659b7d --- /dev/null +++ b/x-pack/elastic-agent/pkg/artifact/install/atomic/atomic_installer_test.go @@ -0,0 +1,115 @@ +// 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 atomic + +import ( + "context" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sync" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestOKInstall(t *testing.T) { + sig := make(chan int) + ti := &testInstaller{sig} + var wg sync.WaitGroup + i, err := NewInstaller(ti) + + assert.NoError(t, err) + + ctx := context.Background() + installDir := filepath.Join(os.TempDir(), "install_dir") + + wg.Add(1) + go func() { + err := i.Install(ctx, "a", "b", installDir) + assert.NoError(t, err) + wg.Done() + }() + + // signal to process next files + close(sig) + + wg.Wait() + + assert.DirExists(t, installDir) + files := getFiles() + + for name := range files { + path := filepath.Join(installDir, name) + assert.FileExists(t, path) + } + + os.RemoveAll(installDir) +} + +func TestContextCancelledInstall(t *testing.T) { + sig := make(chan int) + ti := &testInstaller{sig} + var wg sync.WaitGroup + i, err := NewInstaller(ti) + + assert.NoError(t, err) + + ctx, cancel := context.WithCancel(context.Background()) + installDir := filepath.Join(os.TempDir(), "install_dir") + + wg.Add(1) + go func() { + err := i.Install(ctx, "a", "b", installDir) + assert.Error(t, err) + wg.Done() + }() + + // cancel before signaling + cancel() + close(sig) + + wg.Wait() + + assert.NoDirExists(t, installDir) +} + +type testInstaller struct { + signal chan int +} + +func (ti *testInstaller) Install(ctx context.Context, programName, version, installDir string) error { + files := getFiles() + if err := os.MkdirAll(installDir, 0777); err != nil { + return err + } + + for name, content := range files { + if err := ctx.Err(); err != nil { + return err + } + + filename := filepath.Join(installDir, name) + if err := ioutil.WriteFile(filename, content, 0666); err != nil { + return err + } + + // wait for all but last + <-ti.signal + } + + return nil +} + +func getFiles() map[string][]byte { + files := make(map[string][]byte) + fileCount := 3 + for i := 1; i <= fileCount; i++ { + files[fmt.Sprintf("file_%d", i)] = []byte(fmt.Sprintf("content of file %d", i)) + } + + return files +} diff --git a/x-pack/elastic-agent/pkg/artifact/install/installer.go b/x-pack/elastic-agent/pkg/artifact/install/installer.go index f04e7a4238e..c606ada5d65 100644 --- a/x-pack/elastic-agent/pkg/artifact/install/installer.go +++ b/x-pack/elastic-agent/pkg/artifact/install/installer.go @@ -12,6 +12,7 @@ import ( "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact/install/dir" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact/install/atomic" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact/install/hooks" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact/install/tar" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact/install/zip" @@ -60,5 +61,10 @@ func NewInstaller(config *artifact.Config) (InstallerChecker, error) { return nil, err } - return hooks.NewInstallerChecker(installer, dir.NewChecker()) + atomicInstaller, err := atomic.NewInstaller(installer) + if err != nil { + return nil, err + } + + return hooks.NewInstallerChecker(atomicInstaller, dir.NewChecker()) } From a74c74f3e5fdee905c1521edb5ad2d64b2d3a672 Mon Sep 17 00:00:00 2001 From: Michal Pristas Date: Wed, 14 Oct 2020 10:17:56 +0200 Subject: [PATCH 84/93] [Ingest Manager] Atomic installed forgotten changelog #21780 --- x-pack/elastic-agent/CHANGELOG.next.asciidoc | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/elastic-agent/CHANGELOG.next.asciidoc b/x-pack/elastic-agent/CHANGELOG.next.asciidoc index eb98ef39ded..96f036dd15d 100644 --- a/x-pack/elastic-agent/CHANGELOG.next.asciidoc +++ b/x-pack/elastic-agent/CHANGELOG.next.asciidoc @@ -15,6 +15,7 @@ - Copy Action store on upgrade {pull}21298[21298] - Include inputs in action store actions {pull}21298[21298] - Fix issue where inputs without processors defined would panic {pull}21628[21628] +- Partial extracted beat result in failure to spawn beat {issue}21718[21718] ==== New features From e3cf9939f97d77f6efd848b1786cbf2e2a963f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 14 Oct 2020 11:23:48 +0200 Subject: [PATCH 85/93] chore: create CI artifacts for DEV usage (#21645) It will create the artifacts with some requirements related to integrity --- .ci/packaging.groovy | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.ci/packaging.groovy b/.ci/packaging.groovy index 9087cf48de4..37eeaa7d223 100644 --- a/.ci/packaging.groovy +++ b/.ci/packaging.groovy @@ -246,8 +246,12 @@ def runE2ETestForPackages(){ def release(){ withBeatsEnv(){ - dir("${env.BEATS_FOLDER}") { - sh(label: "Release ${env.BEATS_FOLDER} ${env.PLATFORMS}", script: 'mage package') + withEnv([ + "DEV=true" + ]) { + dir("${env.BEATS_FOLDER}") { + sh(label: "Release ${env.BEATS_FOLDER} ${env.PLATFORMS}", script: 'mage package') + } } publishPackages("${env.BEATS_FOLDER}") } From 303133990cbe6c001d3242445ab03a2d290c4883 Mon Sep 17 00:00:00 2001 From: Pavel Derendyaev Date: Wed, 14 Oct 2020 12:35:30 +0300 Subject: [PATCH 86/93] typofix for dns timeout configuration option (#21069) --- libbeat/processors/dns/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libbeat/processors/dns/config.go b/libbeat/processors/dns/config.go index 8b0d6c9bd97..a2c2a98955e 100644 --- a/libbeat/processors/dns/config.go +++ b/libbeat/processors/dns/config.go @@ -31,7 +31,7 @@ import ( type Config struct { CacheConfig `config:",inline"` Nameservers []string `config:"nameservers"` // Required on Windows. /etc/resolv.conf is used if none are given. - Timeout time.Duration `conifg:"timeout"` // Per request timeout (with 2 nameservers the total timeout would be 2x). + Timeout time.Duration `config:"timeout"` // Per request timeout (with 2 nameservers the total timeout would be 2x). Type string `config:"type" validate:"required"` // Reverse is the only supported type currently. Action FieldAction `config:"action"` // Append or replace (defaults to append) when target exists. TagOnFailure []string `config:"tag_on_failure"` // Tags to append when a failure occurs. From 6c80fb3b37df033adcaffa190208e5c1d6e88d7d Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Wed, 14 Oct 2020 12:00:07 +0200 Subject: [PATCH 87/93] Increase recommended memory when deploying in Cloud foundry (#21755) --- deploy/cloudfoundry/filebeat/manifest.yml | 2 +- deploy/cloudfoundry/metricbeat/manifest.yml | 2 +- filebeat/docs/running-on-cloudfoundry.asciidoc | 2 +- metricbeat/docs/running-on-cloudfoundry.asciidoc | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deploy/cloudfoundry/filebeat/manifest.yml b/deploy/cloudfoundry/filebeat/manifest.yml index 26ec5a0b958..5c424823cb3 100644 --- a/deploy/cloudfoundry/filebeat/manifest.yml +++ b/deploy/cloudfoundry/filebeat/manifest.yml @@ -1,6 +1,6 @@ applications: - name: filebeat - memory: 256M + memory: 512M instances: 1 buildpacks: - binary_buildpack diff --git a/deploy/cloudfoundry/metricbeat/manifest.yml b/deploy/cloudfoundry/metricbeat/manifest.yml index 40f0459b8da..1a2bb025683 100644 --- a/deploy/cloudfoundry/metricbeat/manifest.yml +++ b/deploy/cloudfoundry/metricbeat/manifest.yml @@ -1,6 +1,6 @@ applications: - name: metricbeat - memory: 256M + memory: 512M instances: 1 buildpacks: - binary_buildpack diff --git a/filebeat/docs/running-on-cloudfoundry.asciidoc b/filebeat/docs/running-on-cloudfoundry.asciidoc index ae9603dc012..c08efa30c5f 100644 --- a/filebeat/docs/running-on-cloudfoundry.asciidoc +++ b/filebeat/docs/running-on-cloudfoundry.asciidoc @@ -66,7 +66,7 @@ To check the status, run: $ cf apps name requested state instances memory disk urls -filebeat started 1/1 256M 1G +filebeat started 1/1 512M 1G ------------------------------------------------ Log events should start flowing to Elasticsearch. The events are annotated with diff --git a/metricbeat/docs/running-on-cloudfoundry.asciidoc b/metricbeat/docs/running-on-cloudfoundry.asciidoc index 2988e4d3a8b..5fdf20c9f72 100644 --- a/metricbeat/docs/running-on-cloudfoundry.asciidoc +++ b/metricbeat/docs/running-on-cloudfoundry.asciidoc @@ -66,7 +66,7 @@ To check the status, run: $ cf apps name requested state instances memory disk urls -metricbeat started 1/1 256M 1G +metricbeat started 1/1 512M 1G ------------------------------------------------ Metrics should start flowing to Elasticsearch. The events are annotated with From 1685f972d39607203a65eb9208941a3bc9d87a86 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Wed, 14 Oct 2020 11:03:28 +0100 Subject: [PATCH 88/93] [CI] Support skip-ci label (#21377) --- Jenkinsfile | 14 +++++++++----- Jenkinsfile.yml | 9 ++++----- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 17041987b27..30564270125 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -103,13 +103,17 @@ pipeline { script { def mapParallelTasks = [:] def content = readYaml(file: 'Jenkinsfile.yml') - content['projects'].each { projectName -> - generateStages(project: projectName, changeset: content['changeset']).each { k,v -> - mapParallelTasks["${k}"] = v + if (content?.disabled?.when?.labels && beatsWhen(project: 'top-level', content: content?.disabled?.when)) { + error 'Pull Request has been configured to be disabled when there is a skip-ci label match' + } else { + content['projects'].each { projectName -> + generateStages(project: projectName, changeset: content['changeset']).each { k,v -> + mapParallelTasks["${k}"] = v + } } + notifyBuildReason() + parallel(mapParallelTasks) } - notifyBuildReason() - parallel(mapParallelTasks) } } } diff --git a/Jenkinsfile.yml b/Jenkinsfile.yml index 2278ea93735..f7b21e1cbdf 100644 --- a/Jenkinsfile.yml +++ b/Jenkinsfile.yml @@ -40,10 +40,9 @@ changeset: - "^testing/.*" - "^x-pack/libbeat/.*" -## Proposal -## TBC: This will allow to configure what to do based on the PR configuration disabled: when: - labels: ## Skip the GitHub Pull Request builds if there is a GitHub label match - - "skip-ci" - draft: true ## Skip the GitHub Pull Request builds with Draft PRs. + labels: ## Skip the GitHub Pull Request builds if any of the given GitHub labels match with the assigned labels in the PR. + - skip-ci + ## TODO: This will allow to configure what to do based on the PR configuration + draft: true ## Skip the GitHub Pull Request builds with Draft PRs. \ No newline at end of file From 3e5a167809210e9999732d5e50c7e895e10fc389 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Wed, 14 Oct 2020 12:58:48 +0200 Subject: [PATCH 89/93] Make API address and Shard ID required in Cloud Foundry settings (#21759) Having an auto-generated Shard ID leads to duplicated data when trying to scale, increasing the problems of loaded systems. Forcing to set a shard ID makes the user more conscious of the implications of this setting. API address should be always set in a real deployment. --- CHANGELOG.next.asciidoc | 3 +++ deploy/cloudfoundry/filebeat/filebeat.yml | 2 +- deploy/cloudfoundry/metricbeat/metricbeat.yml | 3 ++- .../docs/running-on-cloudfoundry.asciidoc | 8 ------ metricbeat/docs/modules/cloudfoundry.asciidoc | 3 ++- .../docs/running-on-cloudfoundry.asciidoc | 8 ------ .../docs/inputs/input-cloudfoundry.asciidoc | 2 ++ x-pack/libbeat/common/cloudfoundry/config.go | 13 ++-------- .../common/cloudfoundry/config_test.go | 26 +++++++++++++++++-- .../common/cloudfoundry/test/config.go | 9 ++++++- .../add_cloudfoundry_metadata.go | 6 +++++ x-pack/metricbeat/metricbeat.reference.yml | 1 + .../cloudfoundry/_meta/config.reference.yml | 1 + .../module/cloudfoundry/_meta/config.yml | 1 + .../module/cloudfoundry/_meta/docs.asciidoc | 2 +- .../modules.d/cloudfoundry.yml.disabled | 1 + 16 files changed, 55 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 41de16190a8..8ff5fbee069 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -25,6 +25,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Allow embedding of CAs, Certificate of private keys for anything that support TLS in ouputs and inputs https://github.com/elastic/beats/pull/21179 - Update to Golang 1.12.1. {pull}11330[11330] - Disable Alibaba Cloud and Tencent Cloud metadata providers by default. {pull}13812[12812] +- API address is a required setting in `add_cloudfoundry_metadata`. {pull}21759[21759] *Auditbeat* @@ -78,6 +79,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Removed experimental modules `citrix`, `kaspersky`, `rapid7` and `tenable`. {pull}20706[20706] - Add support for GMT timezone offsets in `decode_cef`. {pull}20993[20993] - Fix parsing of Elasticsearch node name by `elasticsearch/slowlog` fileset. {pull}14547[14547] +- API address and shard ID are required settings in the Cloud Foundry input. {pull}21759[21759] *Heartbeat* @@ -95,6 +97,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Fix ECS compliance of user.id field in system/users metricset {pull}19019[19019] - Rename googlecloud stackdriver metricset to metrics. {pull}19718[19718] - Remove "invalid zero" metrics on Windows and Darwin, don't report linux-only memory and diskio metrics when running under agent. {pull}21457[21457] +- API address and shard ID are required settings in the Cloud Foundry module. {pull}21759[21759] *Packetbeat* diff --git a/deploy/cloudfoundry/filebeat/filebeat.yml b/deploy/cloudfoundry/filebeat/filebeat.yml index f4c4943d4bb..f3d58f079a1 100644 --- a/deploy/cloudfoundry/filebeat/filebeat.yml +++ b/deploy/cloudfoundry/filebeat/filebeat.yml @@ -11,7 +11,7 @@ filebeat.inputs: #doppler_address: ${DOPPLER_ADDRESS} #uaa_address: ${UAA_ADDRESS} #rlp_address: ${RLP_ADDRESS} - #shard_id: ${SHARD_ID} + shard_id: ${SHARD_ID} #version: v1 diff --git a/deploy/cloudfoundry/metricbeat/metricbeat.yml b/deploy/cloudfoundry/metricbeat/metricbeat.yml index c89c00ce983..12cfee75f00 100644 --- a/deploy/cloudfoundry/metricbeat/metricbeat.yml +++ b/deploy/cloudfoundry/metricbeat/metricbeat.yml @@ -11,7 +11,8 @@ metricbeat.modules: #doppler_address: ${DOPPLER_ADDRESS} #uaa_address: ${UAA_ADDRESS} #rlp_address: ${RLP_ADDRESS} - #shard_id: ${SHARD_ID} + shard_id: ${SHARD_ID} + #version: v1 #================================ Outputs ===================================== diff --git a/filebeat/docs/running-on-cloudfoundry.asciidoc b/filebeat/docs/running-on-cloudfoundry.asciidoc index c08efa30c5f..5aa3ef95837 100644 --- a/filebeat/docs/running-on-cloudfoundry.asciidoc +++ b/filebeat/docs/running-on-cloudfoundry.asciidoc @@ -71,11 +71,3 @@ filebeat started 1/1 512M 1G Log events should start flowing to Elasticsearch. The events are annotated with metadata added by the <> processor. - - -[WARNING] -======================================= -*Set shard_id to scale:* By default {beatname_uc} will generate a random `shard_id` when it starts. In the case that -{beatname_uc} needs to be scaled passed 1 instance, be sure to set a static `shard_id`. Not setting a static `shard_id` -will result in duplicate events being pushed to Elasticsearch. -======================================= diff --git a/metricbeat/docs/modules/cloudfoundry.asciidoc b/metricbeat/docs/modules/cloudfoundry.asciidoc index 4d153933c23..614c703d155 100644 --- a/metricbeat/docs/modules/cloudfoundry.asciidoc +++ b/metricbeat/docs/modules/cloudfoundry.asciidoc @@ -117,7 +117,7 @@ Client Secret to authenticate with Cloud Foundry. Default: "". === `shard_id` Shard ID for connection to the RLP Gateway. Use the same ID across multiple {beatname_lc} to shard the load of events -from the RLP Gateway. Default: "(generated UUID)". +from the RLP Gateway. [float] ==== `version` @@ -152,6 +152,7 @@ metricbeat.modules: rlp_address: '${CLOUDFOUNDRY_RLP_ADDRESS:""}' client_id: '${CLOUDFOUNDRY_CLIENT_ID:""}' client_secret: '${CLOUDFOUNDRY_CLIENT_SECRET:""}' + shard_id: metricbeat version: v1 ---- diff --git a/metricbeat/docs/running-on-cloudfoundry.asciidoc b/metricbeat/docs/running-on-cloudfoundry.asciidoc index 5fdf20c9f72..59802ba505d 100644 --- a/metricbeat/docs/running-on-cloudfoundry.asciidoc +++ b/metricbeat/docs/running-on-cloudfoundry.asciidoc @@ -71,11 +71,3 @@ metricbeat started 1/1 512M 1G Metrics should start flowing to Elasticsearch. The events are annotated with metadata added by the <> processor. - - -[WARNING] -======================================= -*Set shard_id to scale:* By default {beatname_uc} will generate a random `shard_id` when it starts. In the case that -{beatname_uc} needs to be scaled passed 1 instance, be sure to set a static `shard_id`. Not setting a static `shard_id` -will result in duplicate events being pushed to Elasticsearch. -======================================= diff --git a/x-pack/filebeat/docs/inputs/input-cloudfoundry.asciidoc b/x-pack/filebeat/docs/inputs/input-cloudfoundry.asciidoc index f8d5dd51015..551d0095b3a 100644 --- a/x-pack/filebeat/docs/inputs/input-cloudfoundry.asciidoc +++ b/x-pack/filebeat/docs/inputs/input-cloudfoundry.asciidoc @@ -23,6 +23,7 @@ Example configurations: api_address: https://api.dev.cfdev.sh client_id: uaa-filebeat client_secret: verysecret + shard_id: filebeat ssl: verification_mode: none ---- @@ -34,6 +35,7 @@ Example configurations: api_address: https://api.dev.cfdev.sh client_id: uaa-filebeat client_secret: verysecret + shard_id: filebeat ssl.certificate_authorities: ["/etc/pki/cf/ca.pem"] ssl.certificate: "/etc/pki/cf/cert.pem" ssl.key: "/etc/pki/cf/cert.key" diff --git a/x-pack/libbeat/common/cloudfoundry/config.go b/x-pack/libbeat/common/cloudfoundry/config.go index 0724bdc66e1..2f15d0c7cf9 100644 --- a/x-pack/libbeat/common/cloudfoundry/config.go +++ b/x-pack/libbeat/common/cloudfoundry/config.go @@ -10,8 +10,6 @@ import ( "strings" "time" - "github.com/gofrs/uuid" - "github.com/elastic/beats/v7/libbeat/common/transport/tlscommon" ) @@ -32,14 +30,14 @@ type Config struct { TLS *tlscommon.Config `config:"ssl"` // Override URLs returned from the CF client - APIAddress string `config:"api_address"` + APIAddress string `config:"api_address" validate:"required"` DopplerAddress string `config:"doppler_address"` UaaAddress string `config:"uaa_address"` RlpAddress string `config:"rlp_address"` // ShardID when retrieving events from loggregator, sharing this ID across // multiple filebeats will shard the load of receiving and sending events. - ShardID string `config:"shard_id"` + ShardID string `config:"shard_id" validate:"required"` // Maximum amount of time to cache application objects from CF client. CacheDuration time.Duration `config:"cache_duration"` @@ -50,13 +48,6 @@ type Config struct { // InitDefaults initialize the defaults for the configuration. func (c *Config) InitDefaults() { - // If not provided by the user; subscription ID should be a unique string to avoid clustering by default. - // Default to using a UUID4 string. - uuid, err := uuid.NewV4() - if err != nil { - panic(err) - } - c.ShardID = uuid.String() c.CacheDuration = 120 * time.Second c.CacheRetryDelay = 20 * time.Second c.Version = ConsumerVersionV1 diff --git a/x-pack/libbeat/common/cloudfoundry/config_test.go b/x-pack/libbeat/common/cloudfoundry/config_test.go index bce98a0b642..79ba7d47cc1 100644 --- a/x-pack/libbeat/common/cloudfoundry/config_test.go +++ b/x-pack/libbeat/common/cloudfoundry/config_test.go @@ -22,26 +22,48 @@ func TestValidation(t *testing.T) { var noId Config assert.Error(t, ucfg.MustNewFrom(common.MapStr{ + "api_address": "https://api.dev.cfdev.sh", "client_secret": "client_secret", + "shard_id": "beats-test-1", }).Unpack(&noId)) var noSecret Config assert.Error(t, ucfg.MustNewFrom(common.MapStr{ - "client_id": "client_id", + "api_address": "https://api.dev.cfdev.sh", + "client_id": "client_id", + "shard_id": "beats-test-1", }).Unpack(&noSecret)) + var noAPI Config + assert.Error(t, ucfg.MustNewFrom(common.MapStr{ + "client_id": "client_id", + "client_secret": "client_secret", + "shard_id": "beats-test-1", + }).Unpack(&noAPI)) + + var noShardID Config + assert.Error(t, ucfg.MustNewFrom(common.MapStr{ + "api_address": "https://api.dev.cfdev.sh", + "client_id": "client_id", + "client_secret": "client_secret", + }).Unpack(&noShardID)) + var valid Config assert.NoError(t, ucfg.MustNewFrom(common.MapStr{ + "api_address": "https://api.dev.cfdev.sh", "client_id": "client_id", "client_secret": "client_secret", + "shard_id": "beats-test-1", }).Unpack(&valid)) } func TestInitDefaults(t *testing.T) { var cfCfg Config assert.NoError(t, ucfg.MustNewFrom(common.MapStr{ + "api_address": "https://api.dev.cfdev.sh", "client_id": "client_id", "client_secret": "client_secret", + "shard_id": "beats-test-1", }).Unpack(&cfCfg)) - assert.Len(t, cfCfg.ShardID, 36) + assert.Equal(t, ConsumerVersionV1, cfCfg.Version) } diff --git a/x-pack/libbeat/common/cloudfoundry/test/config.go b/x-pack/libbeat/common/cloudfoundry/test/config.go index f7b9cd18ffb..45059fc559c 100644 --- a/x-pack/libbeat/common/cloudfoundry/test/config.go +++ b/x-pack/libbeat/common/cloudfoundry/test/config.go @@ -7,15 +7,23 @@ package test import ( "os" "testing" + + "github.com/gofrs/uuid" ) func GetConfigFromEnv(t *testing.T) map[string]interface{} { t.Helper() + shardID, err := uuid.NewV4() + if err != nil { + t.Fatalf("Unable to create a random shard ID: %v", err) + } + config := map[string]interface{}{ "api_address": lookupEnv(t, "CLOUDFOUNDRY_API_ADDRESS"), "client_id": lookupEnv(t, "CLOUDFOUNDRY_CLIENT_ID"), "client_secret": lookupEnv(t, "CLOUDFOUNDRY_CLIENT_SECRET"), + "shard_id": shardID.String(), "ssl.verification_mode": "none", } @@ -23,7 +31,6 @@ func GetConfigFromEnv(t *testing.T) map[string]interface{} { optionalConfig(config, "uaa_address", "CLOUDFOUNDRY_UAA_ADDRESS") optionalConfig(config, "rlp_address", "CLOUDFOUNDRY_RLP_ADDRESS") optionalConfig(config, "doppler_address", "CLOUDFOUNDRY_DOPPLER_ADDRESS") - optionalConfig(config, "shard_id", "CLOUDFOUNDRY_SHARD_ID") if t.Failed() { t.FailNow() diff --git a/x-pack/libbeat/processors/add_cloudfoundry_metadata/add_cloudfoundry_metadata.go b/x-pack/libbeat/processors/add_cloudfoundry_metadata/add_cloudfoundry_metadata.go index c178ea04325..a6b8bd16566 100644 --- a/x-pack/libbeat/processors/add_cloudfoundry_metadata/add_cloudfoundry_metadata.go +++ b/x-pack/libbeat/processors/add_cloudfoundry_metadata/add_cloudfoundry_metadata.go @@ -5,6 +5,7 @@ package add_cloudfoundry_metadata import ( + "github.com/gofrs/uuid" "github.com/pkg/errors" "github.com/elastic/beats/v7/libbeat/beat" @@ -32,6 +33,11 @@ const selector = "add_cloudfoundry_metadata" // New constructs a new add_cloudfoundry_metadata processor. func New(cfg *common.Config) (processors.Processor, error) { var config cloudfoundry.Config + + // ShardID is required in cloudfoundry config to consume from the firehose, + // but not for metadata requests, randomly generate one and use it. + config.ShardID = uuid.Must(uuid.NewV4()).String() + if err := cfg.Unpack(&config); err != nil { return nil, errors.Wrapf(err, "fail to unpack the %v configuration", processorName) } diff --git a/x-pack/metricbeat/metricbeat.reference.yml b/x-pack/metricbeat/metricbeat.reference.yml index b7b62643353..41552410a38 100644 --- a/x-pack/metricbeat/metricbeat.reference.yml +++ b/x-pack/metricbeat/metricbeat.reference.yml @@ -398,6 +398,7 @@ metricbeat.modules: rlp_address: '${CLOUDFOUNDRY_RLP_ADDRESS:""}' client_id: '${CLOUDFOUNDRY_CLIENT_ID:""}' client_secret: '${CLOUDFOUNDRY_CLIENT_SECRET:""}' + shard_id: metricbeat version: v1 #----------------------------- CockroachDB Module ----------------------------- diff --git a/x-pack/metricbeat/module/cloudfoundry/_meta/config.reference.yml b/x-pack/metricbeat/module/cloudfoundry/_meta/config.reference.yml index be15db23b65..6299cfc8116 100644 --- a/x-pack/metricbeat/module/cloudfoundry/_meta/config.reference.yml +++ b/x-pack/metricbeat/module/cloudfoundry/_meta/config.reference.yml @@ -10,4 +10,5 @@ rlp_address: '${CLOUDFOUNDRY_RLP_ADDRESS:""}' client_id: '${CLOUDFOUNDRY_CLIENT_ID:""}' client_secret: '${CLOUDFOUNDRY_CLIENT_SECRET:""}' + shard_id: metricbeat version: v1 diff --git a/x-pack/metricbeat/module/cloudfoundry/_meta/config.yml b/x-pack/metricbeat/module/cloudfoundry/_meta/config.yml index a2803b06d40..5ea86f3e8de 100644 --- a/x-pack/metricbeat/module/cloudfoundry/_meta/config.yml +++ b/x-pack/metricbeat/module/cloudfoundry/_meta/config.yml @@ -7,3 +7,4 @@ api_address: '${CLOUDFOUNDRY_API_ADDRESS:""}' client_id: '${CLOUDFOUNDRY_CLIENT_ID:""}' client_secret: '${CLOUDFOUNDRY_CLIENT_SECRET:""}' + shard_id: metricbeat diff --git a/x-pack/metricbeat/module/cloudfoundry/_meta/docs.asciidoc b/x-pack/metricbeat/module/cloudfoundry/_meta/docs.asciidoc index 4d908802358..752b2aaea0c 100644 --- a/x-pack/metricbeat/module/cloudfoundry/_meta/docs.asciidoc +++ b/x-pack/metricbeat/module/cloudfoundry/_meta/docs.asciidoc @@ -107,7 +107,7 @@ Client Secret to authenticate with Cloud Foundry. Default: "". === `shard_id` Shard ID for connection to the RLP Gateway. Use the same ID across multiple {beatname_lc} to shard the load of events -from the RLP Gateway. Default: "(generated UUID)". +from the RLP Gateway. [float] ==== `version` diff --git a/x-pack/metricbeat/modules.d/cloudfoundry.yml.disabled b/x-pack/metricbeat/modules.d/cloudfoundry.yml.disabled index ae540f20cfc..22a600e51d8 100644 --- a/x-pack/metricbeat/modules.d/cloudfoundry.yml.disabled +++ b/x-pack/metricbeat/modules.d/cloudfoundry.yml.disabled @@ -10,3 +10,4 @@ api_address: '${CLOUDFOUNDRY_API_ADDRESS:""}' client_id: '${CLOUDFOUNDRY_CLIENT_ID:""}' client_secret: '${CLOUDFOUNDRY_CLIENT_SECRET:""}' + shard_id: metricbeat From 85b95ee14300324f4950cdca13df1e04e84cc330 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Wed, 14 Oct 2020 13:15:39 +0200 Subject: [PATCH 90/93] Disable writes sync in persistent cache (#21754) With default configuration, every write is synced to disk. For the persistent cache, Beats not need to guarantee that everything is on disk, as the data can be recovered from the source endpoints, but the database was not being properly closed, so there could happen that most of the recent data was lost if the beat is stopped. Now, the processors can close their resources, so the database is properly closed in an normal shutdown, so we can go on without syncs on writes, what gives a much better performance. --- x-pack/libbeat/persistentcache/store.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/x-pack/libbeat/persistentcache/store.go b/x-pack/libbeat/persistentcache/store.go index 589fc724e01..e14b90fedda 100644 --- a/x-pack/libbeat/persistentcache/store.go +++ b/x-pack/libbeat/persistentcache/store.go @@ -36,10 +36,7 @@ func newStore(logger *logp.Logger, dir, name string) (*Store, error) { // Opinionated options for the use of badger as a store for metadata caches in Beats. options := badger.DefaultOptions(dbPath) options.Logger = badgerLogger{logger.Named("badger")} - // TODO: Disabling sync writes gives better performance, and data loss wouldn't - // be a problem for caches. But we are not properly closing processors yet, so let - // sync on writes by now. - // options.SyncWrites = false + options.SyncWrites = false db, err := badger.Open(options) if err != nil { From 355eef4f4bf1081568a0cd15bda16d17cd628fb2 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Wed, 14 Oct 2020 13:59:52 +0200 Subject: [PATCH 91/93] Use badger code from upstream repository (#21705) Issue in badger with 32-bits architectures has been solved in the release branch used by Beats. Update to the version with this fix and stop using the Elastic fork. --- NOTICE.txt | 6 +++--- go.mod | 3 +-- go.sum | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/NOTICE.txt b/NOTICE.txt index b62c4b5d202..349fe58b3d1 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -4473,12 +4473,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- -Dependency : github.com/elastic/badger/v2 -Version: v2.2007.2-beats +Dependency : github.com/dgraph-io/badger/v2 +Version: v2.2007.3-0.20201012072640-f5a7e0a1c83b Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/elastic/badger/v2@v2.2007.2-beats/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/dgraph-io/badger/v2@v2.2007.3-0.20201012072640-f5a7e0a1c83b/LICENSE: Apache License Version 2.0, January 2004 diff --git a/go.mod b/go.mod index 0f0bdb3a422..720690f1f2f 100644 --- a/go.mod +++ b/go.mod @@ -45,7 +45,7 @@ require ( github.com/davecgh/go-xdr v0.0.0-20161123171359-e6a2ba005892 // indirect github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e github.com/devigned/tab v0.1.2-0.20190607222403-0c15cf42f9a2 // indirect - github.com/dgraph-io/badger/v2 v2.2007.2 + github.com/dgraph-io/badger/v2 v2.2007.3-0.20201012072640-f5a7e0a1c83b github.com/dgrijalva/jwt-go v3.2.1-0.20190620180102-5e25c22bd5d6+incompatible // indirect github.com/digitalocean/go-libvirt v0.0.0-20180301200012-6075ea3c39a1 github.com/dlclark/regexp2 v1.1.7-0.20171009020623-7632a260cbaf // indirect @@ -191,7 +191,6 @@ replace ( github.com/Azure/go-autorest => github.com/Azure/go-autorest v12.2.0+incompatible github.com/Shopify/sarama => github.com/elastic/sarama v1.19.1-0.20200629123429-0e7b69039eec github.com/cucumber/godog => github.com/cucumber/godog v0.8.1 - github.com/dgraph-io/badger/v2 => github.com/elastic/badger/v2 v2.2007.2-beats github.com/docker/docker => github.com/docker/engine v0.0.0-20191113042239-ea84732a7725 github.com/docker/go-plugins-helpers => github.com/elastic/go-plugins-helpers v0.0.0-20200207104224-bdf17607b79f github.com/dop251/goja => github.com/andrewkroh/goja v0.0.0-20190128172624-dd2ac4456e20 diff --git a/go.sum b/go.sum index 6f737d7cee2..5c01c612fe3 100644 --- a/go.sum +++ b/go.sum @@ -201,6 +201,8 @@ github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e/go.mod h1:xb github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= github.com/devigned/tab v0.1.2-0.20190607222403-0c15cf42f9a2 h1:6+hM8KeYKV0Z9EIINNqIEDyyIRAcNc2FW+/TUYNmWyw= github.com/devigned/tab v0.1.2-0.20190607222403-0c15cf42f9a2/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= +github.com/dgraph-io/badger/v2 v2.2007.3-0.20201012072640-f5a7e0a1c83b h1:mUDs72Rlzv6A4YN8w3Ra3hU9x/plOQPcQjZYL/1f5SM= +github.com/dgraph-io/badger/v2 v2.2007.3-0.20201012072640-f5a7e0a1c83b/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de h1:t0UHb5vdojIDUqktM6+xJAfScFBsVpXZmqC9dsgJmeA= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -239,8 +241,6 @@ github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/eclipse/paho.mqtt.golang v1.2.1-0.20200121105743-0d940dd29fd2 h1:DW6WrARxK5J+o8uAKCiACi5wy9EK1UzrsCpGBPsKHAA= github.com/eclipse/paho.mqtt.golang v1.2.1-0.20200121105743-0d940dd29fd2/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= -github.com/elastic/badger/v2 v2.2007.2-beats h1:/rV4bM6fdYvPQhFf2bHHivIb0H4nX8Or7nkWbQ/Q6Ko= -github.com/elastic/badger/v2 v2.2007.2-beats/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/elastic/dhcp v0.0.0-20200227161230-57ec251c7eb3 h1:lnDkqiRFKm0rxdljqrj3lotWinO9+jFmeDXIC4gvIQs= github.com/elastic/dhcp v0.0.0-20200227161230-57ec251c7eb3/go.mod h1:aPqzac6AYkipvp4hufTyMj5PDIphF3+At8zr7r51xjY= github.com/elastic/ecs v1.6.0 h1:8NmgfnsjmKXh9hVsK3H2tZtfUptepNc3msJOAynhtmc= From 3f57f0ead23180dd721858bb40f2c63accb9007a Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Wed, 14 Oct 2020 14:52:02 +0100 Subject: [PATCH 92/93] disable TestReceiveEventsAndMetadata/TestSocketCleanup/TestReceiveNewEventsConcurrently in Windows (#21750) --- filebeat/Jenkinsfile.yml | 12 +++++++++++- filebeat/inputsource/unix/server_test.go | 12 ++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/filebeat/Jenkinsfile.yml b/filebeat/Jenkinsfile.yml index 6b5a23b7fb2..ffe2cec98b4 100644 --- a/filebeat/Jenkinsfile.yml +++ b/filebeat/Jenkinsfile.yml @@ -33,4 +33,14 @@ stages: mage: "mage build unitTest" platforms: ## override default labels in this specific stage. - "windows-2019" - #- "windows-2016" https://github.com/elastic/beats/issues/19641 + windows-2016: + mage: "mage build unitTest" + platforms: ## override default labels in this specific stage. + - "windows-2016" + when: ## Override the top-level when. + comments: + - "/test filebeat for windows-2016" + labels: + - "windows-2016" + branches: true ## for all the branches + tags: true ## for all the tags diff --git a/filebeat/inputsource/unix/server_test.go b/filebeat/inputsource/unix/server_test.go index a9043d14a8e..fc9545100d5 100644 --- a/filebeat/inputsource/unix/server_test.go +++ b/filebeat/inputsource/unix/server_test.go @@ -60,6 +60,10 @@ func TestErrorOnEmptyLineDelimiter(t *testing.T) { } func TestReceiveEventsAndMetadata(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("test is only supported on non-windows. See https://github.com/elastic/beats/issues/19641") + return + } expectedMessages := generateMessages(5, 100) largeMessages := generateMessages(10, 4096) extraLargeMessages := generateMessages(2, 65*1024) @@ -255,6 +259,10 @@ func TestSocketOwnershipAndMode(t *testing.T) { } func TestSocketCleanup(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("test is only supported on non-windows. See https://github.com/elastic/beats/issues/21757") + return + } path := filepath.Join(os.TempDir(), "test.sock") mockStaleSocket, err := net.Listen("unix", path) require.NoError(t, err) @@ -298,6 +306,10 @@ func TestSocketCleanupRefusal(t *testing.T) { } func TestReceiveNewEventsConcurrently(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("test is only supported on non-windows. See https://github.com/elastic/beats/issues/21757") + return + } workers := 4 eventsCount := 100 path := filepath.Join(os.TempDir(), "test.sock") From 4a9b08ae2c67dd9d9631f76e88cf55af54aa3587 Mon Sep 17 00:00:00 2001 From: Alex K <8418476+fearful-symmetry@users.noreply.github.com> Date: Wed, 14 Oct 2020 07:49:01 -0700 Subject: [PATCH 93/93] Fix non-windows fields on system/filesystem (#21758) * fix windows fields on system/filesystem * add changelog * fix tests --- CHANGELOG.next.asciidoc | 1 + metricbeat/module/system/filesystem/helper.go | 4 ++-- metricbeat/module/system/test_system.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 8ff5fbee069..0344f6a90d0 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -371,6 +371,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Add a switch to the driver definition on SQL module to use pretty names {pull}17378[17378] - Fix retrieving resources by ID for the azure module. {pull}21711[21711] {issue}21707[21707] - Use timestamp from CloudWatch API when creating events. {pull}21498[21498] +- Report the correct windows events for system/filesystem {pull}21758[21758] *Packetbeat* diff --git a/metricbeat/module/system/filesystem/helper.go b/metricbeat/module/system/filesystem/helper.go index 4d53ddf2a79..9238df79f59 100644 --- a/metricbeat/module/system/filesystem/helper.go +++ b/metricbeat/module/system/filesystem/helper.go @@ -151,14 +151,14 @@ func GetFilesystemEvent(fsStat *FSStat) common.MapStr { "mount_point": fsStat.Mount, "total": fsStat.Total, "available": fsStat.Avail, - "files": fsStat.Files, + "free": fsStat.Free, "used": common.MapStr{ "pct": fsStat.UsedPercent, "bytes": fsStat.Used, }, } if runtime.GOOS != "windows" { - evt.Put("free", fsStat.Free) + evt.Put("files", fsStat.Files) evt.Put("free_files", fsStat.FreeFiles) } return evt diff --git a/metricbeat/module/system/test_system.py b/metricbeat/module/system/test_system.py index 0cfb820c18a..2e1b9e579d9 100644 --- a/metricbeat/module/system/test_system.py +++ b/metricbeat/module/system/test_system.py @@ -47,7 +47,7 @@ "free_files", "mount_point", "total", "used.bytes", "used.pct"] -SYSTEM_FILESYSTEM_FIELDS_WINDOWS = ["available", "device_name", "type", "files", +SYSTEM_FILESYSTEM_FIELDS_WINDOWS = ["available", "device_name", "type", "free", "mount_point", "total", "used.bytes", "used.pct"]