youtubebeat/vendor/github.com/elastic/beats/packetbeat/publish/publish.go

249 lines
5.7 KiB
Go

// 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 publish
import (
"errors"
"github.com/elastic/beats/libbeat/beat"
"github.com/elastic/beats/libbeat/common"
"github.com/elastic/beats/libbeat/logp"
"github.com/elastic/beats/libbeat/processors"
)
type TransactionPublisher struct {
done chan struct{}
pipeline beat.Pipeline
canDrop bool
processor transProcessor
}
type transProcessor struct {
ignoreOutgoing bool
localIPs []string
name string
}
var debugf = logp.MakeDebug("publish")
func NewTransactionPublisher(
name string,
pipeline beat.Pipeline,
ignoreOutgoing bool,
canDrop bool,
) (*TransactionPublisher, error) {
localIPs, err := common.LocalIPAddrsAsStrings(false)
if err != nil {
return nil, err
}
p := &TransactionPublisher{
done: make(chan struct{}),
pipeline: pipeline,
canDrop: canDrop,
processor: transProcessor{
localIPs: localIPs,
name: name,
ignoreOutgoing: ignoreOutgoing,
},
}
return p, nil
}
func (p *TransactionPublisher) Stop() {
close(p.done)
}
func (p *TransactionPublisher) CreateReporter(
config *common.Config,
) (func(beat.Event), error) {
// load and register the module it's fields, tags and processors settings
meta := struct {
Event common.EventMetadata `config:",inline"`
Processors processors.PluginConfig `config:"processors"`
}{}
if err := config.Unpack(&meta); err != nil {
return nil, err
}
processors, err := processors.New(meta.Processors)
if err != nil {
return nil, err
}
clientConfig := beat.ClientConfig{
EventMetadata: meta.Event,
Processor: processors,
}
if p.canDrop {
clientConfig.PublishMode = beat.DropIfFull
}
client, err := p.pipeline.ConnectWith(clientConfig)
if err != nil {
return nil, err
}
// start worker, so post-processing and processor-pipeline
// can work concurrently to sniffer acquiring new events
ch := make(chan beat.Event, 3)
go p.worker(ch, client)
return func(event beat.Event) {
select {
case ch <- event:
case <-p.done:
ch = nil // stop serving more send requests
}
}, nil
}
func (p *TransactionPublisher) worker(ch chan beat.Event, client beat.Client) {
for {
select {
case <-p.done:
return
case event := <-ch:
pub, _ := p.processor.Run(&event)
if pub != nil {
client.Publish(*pub)
}
}
}
}
func (p *transProcessor) Run(event *beat.Event) (*beat.Event, error) {
if err := validateEvent(event); err != nil {
logp.Warn("Dropping invalid event: %v", err)
return nil, nil
}
if !p.normalizeTransAddr(event.Fields) {
return nil, nil
}
return event, nil
}
// filterEvent validates an event for common required fields with types.
// If event is to be filtered out the reason is returned as error.
func validateEvent(event *beat.Event) error {
fields := event.Fields
if event.Timestamp.IsZero() {
return errors.New("missing '@timestamp'")
}
_, ok := fields["@timestamp"]
if ok {
return errors.New("duplicate '@timestamp' field from event")
}
t, ok := fields["type"]
if !ok {
return errors.New("missing 'type' field from event")
}
_, ok = t.(string)
if !ok {
return errors.New("invalid 'type' field from event")
}
return nil
}
func (p *transProcessor) normalizeTransAddr(event common.MapStr) bool {
debugf("normalize address for: %v", event)
var srcServer, dstServer string
src, ok := event["src"].(*common.Endpoint)
debugf("has src: %v", ok)
if ok {
// check if it's outgoing transaction (as client)
isOutgoing := p.IsPublisherIP(src.IP)
if isOutgoing {
if p.ignoreOutgoing {
// duplicated transaction -> ignore it
debugf("Ignore duplicated transaction on: %s -> %s", srcServer, dstServer)
return false
}
//outgoing transaction
event["direction"] = "out"
}
event["client_ip"] = src.IP
event["client_port"] = src.Port
event["client_proc"] = src.Proc
if len(src.Cmdline) > 0 {
event["client_cmdline"] = src.Cmdline
}
if _, exists := event["client_server"]; !exists {
event["client_server"] = p.GetServerName(src.IP)
}
delete(event, "src")
}
dst, ok := event["dst"].(*common.Endpoint)
debugf("has dst: %v", ok)
if ok {
event["ip"] = dst.IP
event["port"] = dst.Port
event["proc"] = dst.Proc
if len(dst.Cmdline) > 0 {
event["cmdline"] = dst.Cmdline
}
if _, exists := event["server"]; !exists {
event["server"] = p.GetServerName(dst.IP)
}
delete(event, "dst")
//check if it's incoming transaction (as server)
if p.IsPublisherIP(dst.IP) {
// incoming transaction
event["direction"] = "in"
}
}
return true
}
func (p *transProcessor) IsPublisherIP(ip string) bool {
for _, myip := range p.localIPs {
if myip == ip {
return true
}
}
return false
}
func (p *transProcessor) GetServerName(ip string) string {
// in case the IP is localhost, return current shipper name
islocal, err := common.IsLoopback(ip)
if err != nil {
logp.Err("Parsing IP %s fails with: %s", ip, err)
return ""
}
if islocal {
return p.name
}
return ""
}