Path: blob/dev/pkg/reporting/exporters/es/elasticsearch.go
2070 views
package es12import (3"bytes"4"crypto/tls"5"encoding/base64"6"fmt"7"io"8"net/http"9"time"1011"github.com/pkg/errors"1213"github.com/projectdiscovery/nuclei/v3/pkg/output"14"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"15"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"16"github.com/projectdiscovery/retryablehttp-go"17"github.com/projectdiscovery/useragent"18)1920// Options contains necessary options required for elasticsearch communication21type Options struct {22// Host is the hostname of the elasticsearch instance23Host string `yaml:"host" validate:"required_without=IP"`24// IP for elasticsearch instance25IP string `yaml:"ip" validate:"required,ip"`26// Port is the port of elasticsearch instance27Port int `yaml:"port" validate:"gte=0,lte=65535"`28// SSL (optional) enables ssl for elasticsearch connection29SSL bool `yaml:"ssl"`30// SSLVerification (optional) disables SSL verification for elasticsearch31SSLVerification bool `yaml:"ssl-verification"`32// Username for the elasticsearch instance33Username string `yaml:"username" validate:"required"`34// Password is the password for elasticsearch instance35Password string `yaml:"password" validate:"required"`36// IndexName is the name of the elasticsearch index37IndexName string `yaml:"index-name" validate:"required"`3839HttpClient *retryablehttp.Client `yaml:"-"`40ExecutionId string `yaml:"-"`41}4243type data struct {44Event *output.ResultEvent `json:"event"`45Timestamp string `json:"@timestamp"`46}4748// Exporter type for elasticsearch49type Exporter struct {50url string51authentication string52elasticsearch *http.Client53}5455// New creates and returns a new exporter for elasticsearch56func New(option *Options) (*Exporter, error) {57var ei *Exporter5859dialers := protocolstate.GetDialersWithId(option.ExecutionId)60if dialers == nil {61return nil, fmt.Errorf("dialers not initialized for %s", option.ExecutionId)62}6364var client *http.Client65if option.HttpClient != nil {66client = option.HttpClient.HTTPClient67} else {68client = &http.Client{69Timeout: 5 * time.Second,70Transport: &http.Transport{71MaxIdleConns: 10,72MaxIdleConnsPerHost: 10,73DialContext: dialers.Fastdialer.Dial,74DialTLSContext: dialers.Fastdialer.DialTLS,75TLSClientConfig: &tls.Config{InsecureSkipVerify: option.SSLVerification},76},77}78}7980// preparing url for elasticsearch81scheme := "http://"82if option.SSL {83scheme = "https://"84}85// if authentication is required86var authentication string87if len(option.Username) > 0 && len(option.Password) > 0 {88auth := base64.StdEncoding.EncodeToString([]byte(option.Username + ":" + option.Password))89auth = "Basic " + auth90authentication = auth91}92var addr string93if option.Host != "" {94addr = option.Host95} else {96addr = option.IP97}98if option.Port != 0 {99addr += fmt.Sprintf(":%d", option.Port)100}101url := fmt.Sprintf("%s%s/%s/_doc", scheme, addr, option.IndexName)102103ei = &Exporter{104url: url,105authentication: authentication,106elasticsearch: client,107}108return ei, nil109}110111// Export exports a passed result event to elasticsearch112func (exporter *Exporter) Export(event *output.ResultEvent) error {113// creating a request114req, err := http.NewRequest(http.MethodPost, exporter.url, nil)115if err != nil {116return errors.Wrap(err, "could not make request")117}118if len(exporter.authentication) > 0 {119req.Header.Add("Authorization", exporter.authentication)120}121userAgent := useragent.PickRandom()122req.Header.Set("User-Agent", userAgent.Raw)123req.Header.Add("Content-Type", "application/json")124125d := data{126Event: event,127Timestamp: time.Now().Format(time.RFC3339),128}129b, err := json.Marshal(&d)130if err != nil {131return err132}133req.Body = io.NopCloser(bytes.NewReader(b))134135res, err := exporter.elasticsearch.Do(req)136if err != nil {137return err138}139defer func() {140_ = res.Body.Close()141}()142143b, err = io.ReadAll(res.Body)144if err != nil {145return errors.New(err.Error() + "error thrown by elasticsearch " + string(b))146}147148if res.StatusCode >= 300 {149return errors.New("elasticsearch responded with an error: " + string(b))150}151return nil152}153154// Close closes the exporter after operation155func (exporter *Exporter) Close() error {156return nil157}158159160