New test
This commit is contained in:
		@ -1,941 +0,0 @@
 | 
				
			|||||||
package cmd
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"log"
 | 
					 | 
				
			||||||
	"net"
 | 
					 | 
				
			||||||
	"sync"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
	"bufio"
 | 
					 | 
				
			||||||
	"errors"
 | 
					 | 
				
			||||||
	"runtime"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"syscall"
 | 
					 | 
				
			||||||
	"net/http"
 | 
					 | 
				
			||||||
	"os/signal"
 | 
					 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/spf13/cobra"
 | 
					 | 
				
			||||||
	"github.com/tabalt/pidfile"
 | 
					 | 
				
			||||||
	"github.com/prometheus/client_golang/prometheus"
 | 
					 | 
				
			||||||
	openldaplog "git.nosd.in/yo/openldap-log-parser"
 | 
					 | 
				
			||||||
	"github.com/prometheus/client_golang/prometheus/promauto"
 | 
					 | 
				
			||||||
	"github.com/prometheus/client_golang/prometheus/promhttp"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func init() {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type (
 | 
					 | 
				
			||||||
  OpenLdapConnection struct {
 | 
					 | 
				
			||||||
		Time           *time.Time   `json:"time"`
 | 
					 | 
				
			||||||
		Hostname       string       `json:"hostname"`
 | 
					 | 
				
			||||||
		Process        string       `json:"process"`
 | 
					 | 
				
			||||||
		Operations     []*Operation `json:"operations"`
 | 
					 | 
				
			||||||
		ClientIp       string       `json:"client_ip"`
 | 
					 | 
				
			||||||
		ClientPort     int          `json:"client_port"`
 | 
					 | 
				
			||||||
		ServerIp       string       `json:"server_ip"`
 | 
					 | 
				
			||||||
		ServerPort     int          `json:"server_port"`
 | 
					 | 
				
			||||||
		ConnId         int          `json:"conn_id"`
 | 
					 | 
				
			||||||
		ConnFd         int          `json:"conn_fd"`
 | 
					 | 
				
			||||||
		BindDN         *string      `json:"bind_dn"`
 | 
					 | 
				
			||||||
		BindMethod     *string      `json:"bind_method"`
 | 
					 | 
				
			||||||
		BindMech       *string      `json:"bind_mech"`
 | 
					 | 
				
			||||||
		BindSSF        *string      `json:"bind_ssf"`
 | 
					 | 
				
			||||||
		SSF            *string      `json:"ssf"`
 | 
					 | 
				
			||||||
		StartTLS       bool         `json:"starttls"`
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Operation struct {
 | 
					 | 
				
			||||||
		Time           *time.Time `json:"time"`
 | 
					 | 
				
			||||||
		OpType         string     `json:"op_type"`
 | 
					 | 
				
			||||||
		OpId           *int       `json:"op_id,omitempty"`
 | 
					 | 
				
			||||||
		BindDN         string     `json:"bind_dn,omitempty"`
 | 
					 | 
				
			||||||
		BindMethod     string     `json:"bind_method,omitempty"`
 | 
					 | 
				
			||||||
		BindMech       string     `json:"bind_mech,omitempty"`
 | 
					 | 
				
			||||||
		BindSSF        string     `json:"bind_ssf,omitempty"`
 | 
					 | 
				
			||||||
		SSF            string     `json:"ssf,omitempty"`
 | 
					 | 
				
			||||||
		ModDN          string     `json:"mod_dn,omitempty"`
 | 
					 | 
				
			||||||
		ModAttr        string     `json:"mod_attr,omitempty"`
 | 
					 | 
				
			||||||
		PassModDN      string     `json:"passmod_dn,omitempty"`
 | 
					 | 
				
			||||||
		ResTag         string     `json:"result_tag,omitempty"`
 | 
					 | 
				
			||||||
		ResOid         string     `json:"result_oid,omitempty"`
 | 
					 | 
				
			||||||
		// To use "omitempty" on int, they have to be pointers 
 | 
					 | 
				
			||||||
		// This way it willl be displayed when set to 0, and not display when not set (null)
 | 
					 | 
				
			||||||
		ResErr         *int        `json:"result_err,omitempty"`
 | 
					 | 
				
			||||||
		ResQTime       string     `json:"result_qtime,omitempty"`
 | 
					 | 
				
			||||||
		ResETime       string     `json:"result_etime,omitempty"`
 | 
					 | 
				
			||||||
		ResNEntries    *int        `json:"result_nentries,omitempty"`
 | 
					 | 
				
			||||||
		ResText        string     `json:"result_text,omitempty"`
 | 
					 | 
				
			||||||
		SearchBase     string     `json:"search_base,omitempty"`
 | 
					 | 
				
			||||||
		SearchScope    string     `json:"search_scope,omitempty"`
 | 
					 | 
				
			||||||
		SearchDeref    string     `json:"search_deref,omitempty"`
 | 
					 | 
				
			||||||
		SearchFilter   string     `json:"search_filter,omitempty"`
 | 
					 | 
				
			||||||
		SearchAttr     string     `json:"search_attr,omitempty"`
 | 
					 | 
				
			||||||
		SearchResTag   string     `json:"search_res_tag,omitempty"`
 | 
					 | 
				
			||||||
		SearchResErr   *int        `json:"search_res_err,omitempty"`
 | 
					 | 
				
			||||||
		SearchResQTime string     `json:"search_res_qtime,omitempty"`
 | 
					 | 
				
			||||||
		SearchResETime string     `json:"search_res_etime,omitempty"`
 | 
					 | 
				
			||||||
		SearchResNEntries *int     `json:"search_res_nentries,omitempty"`
 | 
					 | 
				
			||||||
		SearchResText  string     `json:"search_res_text,omitempty"`
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	OpenLdapConnectionFlat struct {
 | 
					 | 
				
			||||||
		Time           *time.Time `json:"time"`
 | 
					 | 
				
			||||||
		Hostname       string     `json:"hostname"`
 | 
					 | 
				
			||||||
		Process        string     `json:"process"`
 | 
					 | 
				
			||||||
		ClientIp       string     `json:"client_ip"`
 | 
					 | 
				
			||||||
		ClientPort     int        `json:"client_port"`
 | 
					 | 
				
			||||||
		ServerIp       string     `json:"server_ip"`
 | 
					 | 
				
			||||||
		ServerPort     int        `json:"server_port"`
 | 
					 | 
				
			||||||
		BindDN         string     `json:"bind_dn,omitempty"`
 | 
					 | 
				
			||||||
		ConnId         int        `json:"conn_id"`
 | 
					 | 
				
			||||||
		ConnFd         int        `json:"conn_fd"`
 | 
					 | 
				
			||||||
		OpId           *int       `json:"op_id,omitempty"`
 | 
					 | 
				
			||||||
		OpType         string     `json:"op_type"`
 | 
					 | 
				
			||||||
		BindMethod     string     `json:"bind_method,omitempty"`
 | 
					 | 
				
			||||||
		BindMech       string     `json:"bind_mech,omitempty"`
 | 
					 | 
				
			||||||
		BindSSF        string     `json:"bind_ssf,omitempty"`
 | 
					 | 
				
			||||||
		SSF            string     `json:"ssf,omitempty"`
 | 
					 | 
				
			||||||
		StartTLS       bool       `json:"starttls,omitempty"`
 | 
					 | 
				
			||||||
		ModDN          string     `json:"mod_dn,omitempty"`
 | 
					 | 
				
			||||||
		ModAttr        string     `json:"mod_attr,omitempty"`
 | 
					 | 
				
			||||||
		PassModDN      string     `json:"passmod_dn,omitempty"`
 | 
					 | 
				
			||||||
		ResTag         string     `json:"result_tag,omitempty"`
 | 
					 | 
				
			||||||
		ResOid         string     `json:"result_oid,omitempty"`
 | 
					 | 
				
			||||||
		ResErr         *int       `json:"result_err,omitempty"`
 | 
					 | 
				
			||||||
		ResQTime       string     `json:"result_qtime,omitempty"`
 | 
					 | 
				
			||||||
		ResETime       string     `json:"result_etime,omitempty"`
 | 
					 | 
				
			||||||
		ResText        string     `json:"result_text,omitempty"`
 | 
					 | 
				
			||||||
		SearchBase     string     `json:"search_base,omitempty"`
 | 
					 | 
				
			||||||
		SearchScope    string     `json:"search_scope,omitempty"`
 | 
					 | 
				
			||||||
		SearchDeref    string     `json:"search_deref,omitempty"`
 | 
					 | 
				
			||||||
		SearchFilter   string     `json:"search_filter,omitempty"`
 | 
					 | 
				
			||||||
		SearchAttr     string     `json:"search_attr,omitempty"`
 | 
					 | 
				
			||||||
		SearchResTag   string     `json:"search_res_tag,omitempty"`
 | 
					 | 
				
			||||||
		SearchResErr   *int       `json:"search_res_err,omitempty"`
 | 
					 | 
				
			||||||
		SearchResQTime string     `json:"search_res_qtime,omitempty"`
 | 
					 | 
				
			||||||
		SearchResETime string     `json:"search_res_etime,omitempty"`
 | 
					 | 
				
			||||||
		SearchResNEntries *int    `json:"search_res_nentries,omitempty"`
 | 
					 | 
				
			||||||
		SearchResText  string     `json:"search_res_text,omitempty"`
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var (
 | 
					 | 
				
			||||||
	File   os.File
 | 
					 | 
				
			||||||
	Writer *bufio.Writer
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Version = "0.7.0a"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{
 | 
					 | 
				
			||||||
		Name: "openldaplogparser_build_info",
 | 
					 | 
				
			||||||
		Help: "Constant 1 value labeled by version and goversion from which openldap-log-parser was built",
 | 
					 | 
				
			||||||
	}, []string{"version", "goversion"})
 | 
					 | 
				
			||||||
	StartTime = promauto.NewGauge(prometheus.GaugeOpts{
 | 
					 | 
				
			||||||
		Name: "openldaplogparser_time_start_seconds",
 | 
					 | 
				
			||||||
		Help: "Process start time in UNIX timestamp (seconds)",
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	LineReadCnt = promauto.NewCounter(prometheus.CounterOpts{
 | 
					 | 
				
			||||||
		Name: "openldaplogparser_line_read_count",
 | 
					 | 
				
			||||||
		Help: "Number of lines read",
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	LineIncorrectCnt = promauto.NewCounter(prometheus.CounterOpts{
 | 
					 | 
				
			||||||
		Name: "openldaplogparser_line_incorrect_count",
 | 
					 | 
				
			||||||
		Help: "Number of lines with incorrect format",
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	LineOutCnt = promauto.NewCounterVec(prometheus.CounterOpts{
 | 
					 | 
				
			||||||
		Name: "openldaplogparser_line_out_count",
 | 
					 | 
				
			||||||
		Help: "Number of lines written to ouput",
 | 
					 | 
				
			||||||
	}, []string{"host"})
 | 
					 | 
				
			||||||
	ConnectedClientCnt = promauto.NewGauge(prometheus.GaugeOpts{
 | 
					 | 
				
			||||||
		Name: "openldaplogparser_client_count",
 | 
					 | 
				
			||||||
		Help: "Number of connected clients",
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
  AcceptCnt = promauto.NewCounterVec(prometheus.CounterOpts{
 | 
					 | 
				
			||||||
		Name: "openldaplogparser_accept_count",
 | 
					 | 
				
			||||||
		Help: "Number of ACCEPT commands executed",
 | 
					 | 
				
			||||||
	}, []string{"host"})
 | 
					 | 
				
			||||||
  BindCnt = promauto.NewCounterVec(prometheus.CounterOpts{
 | 
					 | 
				
			||||||
		Name: "openldaplogparser_bind_count",
 | 
					 | 
				
			||||||
		Help: "Number of BIND commands executed",
 | 
					 | 
				
			||||||
	}, []string{"host"})
 | 
					 | 
				
			||||||
  SearchCnt = promauto.NewCounterVec(prometheus.CounterOpts{
 | 
					 | 
				
			||||||
		Name: "openldaplogparser_search_count",
 | 
					 | 
				
			||||||
		Help: "Number of SRCH commands executed",
 | 
					 | 
				
			||||||
	}, []string{"host"})
 | 
					 | 
				
			||||||
  ModCnt = promauto.NewCounterVec(prometheus.CounterOpts{
 | 
					 | 
				
			||||||
		Name: "openldaplogparser_mod_count",
 | 
					 | 
				
			||||||
		Help: "Number of MOD commands executed",
 | 
					 | 
				
			||||||
	}, []string{"host"})
 | 
					 | 
				
			||||||
  PassModCnt = promauto.NewCounterVec(prometheus.CounterOpts{
 | 
					 | 
				
			||||||
		Name: "openldaplogparser_passmod_count",
 | 
					 | 
				
			||||||
		Help: "Number of PASSMOD commands executed",
 | 
					 | 
				
			||||||
	}, []string{"host"})
 | 
					 | 
				
			||||||
  UnbindCnt = promauto.NewCounterVec(prometheus.CounterOpts{
 | 
					 | 
				
			||||||
		Name: "openldaplogparser_unbind_count",
 | 
					 | 
				
			||||||
		Help: "Number of UNBIND commands executed",
 | 
					 | 
				
			||||||
	}, []string{"host"})
 | 
					 | 
				
			||||||
  CloseCnt = promauto.NewCounterVec(prometheus.CounterOpts{
 | 
					 | 
				
			||||||
		Name: "openldaplogparser_close_count",
 | 
					 | 
				
			||||||
		Help: "Number of closed connections",
 | 
					 | 
				
			||||||
	}, []string{"host"})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rootCmd = &cobra.Command{
 | 
					 | 
				
			||||||
		Use:   "openldap-log-parser",
 | 
					 | 
				
			||||||
		Short: "OpenLDAP Log Parser v" + Version + ". Parse openldap log, and output json format",
 | 
					 | 
				
			||||||
		Run: func(cmd *cobra.Command, args []string) {
 | 
					 | 
				
			||||||
			processLogs(cmd, args)
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	gFlatten             bool
 | 
					 | 
				
			||||||
	gOutputFile          string
 | 
					 | 
				
			||||||
	gPidFilePath         string
 | 
					 | 
				
			||||||
	gSyslogListenAddress string
 | 
					 | 
				
			||||||
	gPromListenAddress   string
 | 
					 | 
				
			||||||
	gPromMetricPath      string
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	gDebug               bool
 | 
					 | 
				
			||||||
	gDispUnkConn         bool
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func Execute() {
 | 
					 | 
				
			||||||
	if err := rootCmd.Execute(); err != nil {
 | 
					 | 
				
			||||||
		rootCmd.SetOutput(os.Stderr)
 | 
					 | 
				
			||||||
		rootCmd.Println(err)
 | 
					 | 
				
			||||||
		os.Exit(1)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Flatten OpenLdapConnection by creating an item for each operation
 | 
					 | 
				
			||||||
func OlcToFlat(olc *OpenLdapConnection) []OpenLdapConnectionFlat {
 | 
					 | 
				
			||||||
	var olcf = make([]OpenLdapConnectionFlat, len(olc.Operations))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i := range olc.Operations {
 | 
					 | 
				
			||||||
		olcf[i] = OpenLdapConnectionFlat{
 | 
					 | 
				
			||||||
			Time:           olc.Time,
 | 
					 | 
				
			||||||
			Hostname:       olc.Hostname,
 | 
					 | 
				
			||||||
			Process:        olc.Process,
 | 
					 | 
				
			||||||
			ClientIp:       olc.ClientIp,
 | 
					 | 
				
			||||||
			ClientPort:     olc.ClientPort,
 | 
					 | 
				
			||||||
			ServerIp:       olc.ServerIp,
 | 
					 | 
				
			||||||
			ServerPort:     olc.ServerPort,
 | 
					 | 
				
			||||||
			ConnId:         olc.ConnId,
 | 
					 | 
				
			||||||
			ConnFd:         olc.ConnFd,
 | 
					 | 
				
			||||||
			OpType:         olc.Operations[i].OpType,
 | 
					 | 
				
			||||||
			OpId:           olc.Operations[i].OpId,    
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if olc.BindDN != nil {
 | 
					 | 
				
			||||||
			olcf[i].BindDN = *olc.BindDN
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		switch olc.Operations[i].OpType {
 | 
					 | 
				
			||||||
			case "starttls":
 | 
					 | 
				
			||||||
				olcf[i].ResTag = olc.Operations[i].ResTag
 | 
					 | 
				
			||||||
				olcf[i].ResOid = olc.Operations[i].ResOid
 | 
					 | 
				
			||||||
				olcf[i].ResErr = olc.Operations[i].ResErr
 | 
					 | 
				
			||||||
				olcf[i].ResQTime = olc.Operations[i].ResQTime
 | 
					 | 
				
			||||||
				olcf[i].ResETime = olc.Operations[i].ResETime
 | 
					 | 
				
			||||||
				olcf[i].ResText = olc.Operations[i].ResText
 | 
					 | 
				
			||||||
			case "bind":
 | 
					 | 
				
			||||||
				olcf[i].BindMethod = olc.Operations[i].BindMethod
 | 
					 | 
				
			||||||
				olcf[i].BindMech = olc.Operations[i].BindMech
 | 
					 | 
				
			||||||
				olcf[i].BindSSF = olc.Operations[i].BindSSF
 | 
					 | 
				
			||||||
				olcf[i].SSF = olc.Operations[i].SSF
 | 
					 | 
				
			||||||
				olcf[i].ResTag = olc.Operations[i].ResTag
 | 
					 | 
				
			||||||
				olcf[i].ResOid = olc.Operations[i].ResOid
 | 
					 | 
				
			||||||
				olcf[i].ResErr = olc.Operations[i].ResErr
 | 
					 | 
				
			||||||
				olcf[i].ResQTime = olc.Operations[i].ResQTime
 | 
					 | 
				
			||||||
				olcf[i].ResETime = olc.Operations[i].ResETime
 | 
					 | 
				
			||||||
				olcf[i].ResText = olc.Operations[i].ResText
 | 
					 | 
				
			||||||
			case "search":
 | 
					 | 
				
			||||||
				olcf[i].SearchBase = olc.Operations[i].SearchBase
 | 
					 | 
				
			||||||
				olcf[i].SearchScope = olc.Operations[i].SearchScope
 | 
					 | 
				
			||||||
				olcf[i].SearchDeref = olc.Operations[i].SearchDeref
 | 
					 | 
				
			||||||
				olcf[i].SearchFilter = olc.Operations[i].SearchFilter
 | 
					 | 
				
			||||||
				olcf[i].SearchAttr = olc.Operations[i].SearchAttr
 | 
					 | 
				
			||||||
				olcf[i].SearchResTag = olc.Operations[i].SearchResTag
 | 
					 | 
				
			||||||
				olcf[i].SearchResErr = olc.Operations[i].SearchResErr
 | 
					 | 
				
			||||||
				olcf[i].SearchResQTime = olc.Operations[i].SearchResQTime
 | 
					 | 
				
			||||||
				olcf[i].SearchResETime = olc.Operations[i].SearchResETime
 | 
					 | 
				
			||||||
				olcf[i].SearchResNEntries = olc.Operations[i].SearchResNEntries
 | 
					 | 
				
			||||||
				olcf[i].SearchResText = olc.Operations[i].SearchResText
 | 
					 | 
				
			||||||
			case "mod":
 | 
					 | 
				
			||||||
				olcf[i].ModDN = olc.Operations[i].ModDN
 | 
					 | 
				
			||||||
				olcf[i].ModAttr = olc.Operations[i].ModAttr
 | 
					 | 
				
			||||||
				olcf[i].ResTag = olc.Operations[i].ResTag
 | 
					 | 
				
			||||||
				olcf[i].ResOid = olc.Operations[i].ResOid
 | 
					 | 
				
			||||||
				olcf[i].ResErr = olc.Operations[i].ResErr
 | 
					 | 
				
			||||||
				olcf[i].ResQTime = olc.Operations[i].ResQTime
 | 
					 | 
				
			||||||
				olcf[i].ResETime = olc.Operations[i].ResETime
 | 
					 | 
				
			||||||
				olcf[i].ResText = olc.Operations[i].ResText
 | 
					 | 
				
			||||||
			case "passmod":
 | 
					 | 
				
			||||||
				olcf[i].PassModDN = olc.Operations[i].PassModDN
 | 
					 | 
				
			||||||
				olcf[i].ResTag = olc.Operations[i].ResTag
 | 
					 | 
				
			||||||
				olcf[i].ResOid = olc.Operations[i].ResOid
 | 
					 | 
				
			||||||
				olcf[i].ResErr = olc.Operations[i].ResErr
 | 
					 | 
				
			||||||
				olcf[i].ResQTime = olc.Operations[i].ResQTime
 | 
					 | 
				
			||||||
				olcf[i].ResETime = olc.Operations[i].ResETime
 | 
					 | 
				
			||||||
				olcf[i].ResText = olc.Operations[i].ResText
 | 
					 | 
				
			||||||
		}  
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return olcf
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func NewWriter(file string) (*bufio.Writer, *os.File, error) {
 | 
					 | 
				
			||||||
	if len(file) > 0 {
 | 
					 | 
				
			||||||
		var f *os.File
 | 
					 | 
				
			||||||
		var err error
 | 
					 | 
				
			||||||
		if _, err = os.Stat(file); err == nil {
 | 
					 | 
				
			||||||
			f, err = os.OpenFile(file, os.O_APPEND|os.O_WRONLY, 0640)
 | 
					 | 
				
			||||||
		} else if os.IsNotExist(err) {
 | 
					 | 
				
			||||||
			f, err = os.OpenFile(file, os.O_CREATE|os.O_WRONLY, 0640)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return nil, nil, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		Writer = bufio.NewWriter(f)
 | 
					 | 
				
			||||||
		return Writer, f, nil
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		Writer = bufio.NewWriter(os.Stdout)
 | 
					 | 
				
			||||||
		return Writer, nil, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func writeOut(msg string, filename string) error {
 | 
					 | 
				
			||||||
	_, err := fmt.Fprintln(Writer, msg)
 | 
					 | 
				
			||||||
	Writer.Flush()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var tmpOlc OpenLdapConnection
 | 
					 | 
				
			||||||
	json.Unmarshal([]byte(msg), &tmpOlc)
 | 
					 | 
				
			||||||
	LineOutCnt.WithLabelValues(tmpOlc.Hostname).Inc()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Every 24H, remove sent, milter-rejected and deferred that entered queue more than 5 days ago
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
func periodicallyCleanMQueue(mqueue map[int]*PostfixLogParser, mqMtx *sync.Mutex) {
 | 
					 | 
				
			||||||
	var ok int
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for range time.Tick(time.Hour * 24) {
 | 
					 | 
				
			||||||
		// Do we need read lock?
 | 
					 | 
				
			||||||
		for _, inmail := range mqueue {
 | 
					 | 
				
			||||||
			ok = 0
 | 
					 | 
				
			||||||
			// Check all mails were sent (multiple destinations mails)
 | 
					 | 
				
			||||||
			//  or rejected
 | 
					 | 
				
			||||||
			for _, outmail := range inmail.Messages {
 | 
					 | 
				
			||||||
				if outmail.Status == "sent" || outmail.Status == "milter-reject" {
 | 
					 | 
				
			||||||
					ok += 1
 | 
					 | 
				
			||||||
				} else if outmail.Status == "deferred" {
 | 
					 | 
				
			||||||
					if inmail.Time.Add(time.Hour * 5 * 24).Before(time.Now()) {
 | 
					 | 
				
			||||||
						ok += 1
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if ok == len(inmail.Messages) {
 | 
					 | 
				
			||||||
				mqMtx.Lock()
 | 
					 | 
				
			||||||
				delete(mqueue, inmail.MessageId)
 | 
					 | 
				
			||||||
				mqMtx.Unlock()
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func initConfig() {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func init() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rootCmd.Version = Version
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rootCmd.Flags().BoolVarP(&gFlatten, "flatten", "f", false, "Flatten output for using with syslog")
 | 
					 | 
				
			||||||
	rootCmd.Flags().StringVarP(&gOutputFile, "out", "o", "", "Output to file, append if exists")
 | 
					 | 
				
			||||||
	rootCmd.Flags().StringVarP(&gPidFilePath, "pidfile", "p", "", "pid file path")
 | 
					 | 
				
			||||||
	rootCmd.Flags().StringVarP(&gSyslogListenAddress, "syslog.listen-address", "s", "do-not-listen", "Address to listen on for syslog incoming messages. Default is to parse stdin")
 | 
					 | 
				
			||||||
	rootCmd.Flags().StringVarP(&gPromListenAddress, "prom.listen-address", "l", "do-not-listen", "Address to listen on for prometheus metrics")
 | 
					 | 
				
			||||||
	rootCmd.Flags().StringVarP(&gPromMetricPath, "prom.telemetry-path", "m", "/metrics", "Path under which to expose metrics.")
 | 
					 | 
				
			||||||
	rootCmd.Flags().BoolVarP(&gDebug, "debug", "d", false, "debug mode")
 | 
					 | 
				
			||||||
	rootCmd.Flags().BoolVarP(&gDebug, "unknown", "u", false, "display operations without connection (b/c accept was not seen)")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cobra.OnInitialize(initConfig)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * This is the function doing the work.
 | 
					 | 
				
			||||||
 * Each input is stored in a map - indexed by "hostname+conn_id" so we can handle many hosts - 
 | 
					 | 
				
			||||||
 * then written to output when we recognize it as the last line
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
func parseStoreAndWrite(input []byte, mq map[string]*OpenLdapConnection, mqMtx *sync.Mutex, 
 | 
					 | 
				
			||||||
	outfMtx *sync.Mutex, o *openldaplog.OpenldapLog) error {
 | 
					 | 
				
			||||||
	logFormat, err := o.Parse(input)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		// Incorrect line, just skip it
 | 
					 | 
				
			||||||
		if err.Error() == "Error: Line do not match regex" {
 | 
					 | 
				
			||||||
			LineIncorrectCnt.Inc()
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
		2022-07-18T17:18:19.785512+02:00 ldap.domain.org slapd[82581] conn=16113 fd=34 ACCEPT from IP=10.11.12.13:55689 (IP=0.0.0.0:389)
 | 
					 | 
				
			||||||
	*/
 | 
					 | 
				
			||||||
	if logFormat.ClientIp != "" {
 | 
					 | 
				
			||||||
		op := &Operation{
 | 
					 | 
				
			||||||
			Time:           logFormat.Time,
 | 
					 | 
				
			||||||
			OpType:         logFormat.OpType,
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		var ops []*Operation
 | 
					 | 
				
			||||||
		ops = append(ops, op)
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
		olc := &OpenLdapConnection{
 | 
					 | 
				
			||||||
			Time:           logFormat.Time,
 | 
					 | 
				
			||||||
			Hostname:       logFormat.Hostname,
 | 
					 | 
				
			||||||
			Process:        logFormat.Process,
 | 
					 | 
				
			||||||
			ConnId:         logFormat.ConnId,
 | 
					 | 
				
			||||||
			ConnFd:         logFormat.ConnFd,
 | 
					 | 
				
			||||||
			ClientIp:       logFormat.ClientIp,
 | 
					 | 
				
			||||||
			ClientPort:     logFormat.ClientPort,
 | 
					 | 
				
			||||||
			ServerIp:       logFormat.ServerIp,
 | 
					 | 
				
			||||||
			ServerPort:     logFormat.ServerPort,
 | 
					 | 
				
			||||||
			Operations:     ops,
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Dump to stdout if gFlatten...
 | 
					 | 
				
			||||||
		if gFlatten == true {
 | 
					 | 
				
			||||||
			var jsonBytes []byte
 | 
					 | 
				
			||||||
			if gFlatten {
 | 
					 | 
				
			||||||
				jsonBytes, err = json.Marshal(OlcToFlat(olc)[0])
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				log.Fatal(err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			outfMtx.Lock()
 | 
					 | 
				
			||||||
			err = writeOut(string(jsonBytes), gOutputFile)
 | 
					 | 
				
			||||||
			outfMtx.Unlock()
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				log.Fatal(err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			// Then remove operation from OpenLDAPConnection so it wont output again
 | 
					 | 
				
			||||||
			olc.Operations = nil
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
		mqMtx.Lock()
 | 
					 | 
				
			||||||
		mq[fmt.Sprintf("%s:%d", logFormat.Hostname, logFormat.ConnId)] = olc
 | 
					 | 
				
			||||||
		mqMtx.Unlock()
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		AcceptCnt.WithLabelValues(olc.Hostname).Inc()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /*
 | 
					 | 
				
			||||||
    2022-07-18T14:35:17.381223+02:00 ldap.domain.org slapd slapd[82581] conn=16113 op=0 STARTTLS
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    If we don't have the initial connect, we will discard logs
 | 
					 | 
				
			||||||
	*/
 | 
					 | 
				
			||||||
	if logFormat.OpType == "starttls" {
 | 
					 | 
				
			||||||
		opexist := false
 | 
					 | 
				
			||||||
		mqMtx.Lock()
 | 
					 | 
				
			||||||
		if olc, ok := mq[fmt.Sprintf("%s:%d", logFormat.Hostname, logFormat.ConnId)]; ok {
 | 
					 | 
				
			||||||
		// We may be here for the result of STARTTLS operation
 | 
					 | 
				
			||||||
			for i := range olc.Operations {
 | 
					 | 
				
			||||||
				if *olc.Operations[i].OpId == logFormat.OpId {
 | 
					 | 
				
			||||||
					opexist = true
 | 
					 | 
				
			||||||
					break
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if false == opexist {
 | 
					 | 
				
			||||||
				op := &Operation{
 | 
					 | 
				
			||||||
					Time:           logFormat.Time,
 | 
					 | 
				
			||||||
					OpType:         logFormat.OpType,
 | 
					 | 
				
			||||||
					OpId:           &logFormat.OpId,
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				olc.Operations = append(olc.Operations, op)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		mqMtx.Unlock()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
    2022-07-18T17:18:19.785570+02:00 ldap.domain.org slapd[82581] conn=16113 op=1 BIND dn="cn=coincoin,dc=domain,dc=org" method=128
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    If we don't have the initial connect, we will discard logs
 | 
					 | 
				
			||||||
	*/
 | 
					 | 
				
			||||||
	if logFormat.BindDN != "" && logFormat.BindMethod != "" {
 | 
					 | 
				
			||||||
		mqMtx.Lock()
 | 
					 | 
				
			||||||
		if olc, ok := mq[fmt.Sprintf("%s:%d", logFormat.Hostname, logFormat.ConnId)]; ok {
 | 
					 | 
				
			||||||
			// FIXME: What if this bind is not successful?
 | 
					 | 
				
			||||||
			olc.BindDN = &logFormat.BindDN
 | 
					 | 
				
			||||||
			op := &Operation{
 | 
					 | 
				
			||||||
				Time:           logFormat.Time,
 | 
					 | 
				
			||||||
				OpType:         logFormat.OpType,
 | 
					 | 
				
			||||||
				OpId:           &logFormat.OpId,
 | 
					 | 
				
			||||||
				BindDN:         logFormat.BindDN,
 | 
					 | 
				
			||||||
				BindMethod:     logFormat.BindMethod,
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			olc.Operations = append(olc.Operations, op)
 | 
					 | 
				
			||||||
			BindCnt.WithLabelValues(olc.Hostname).Inc()
 | 
					 | 
				
			||||||
    	} else {
 | 
					 | 
				
			||||||
			if gDispUnkConn == true {
 | 
					 | 
				
			||||||
				// use conn_id = 0
 | 
					 | 
				
			||||||
				olc, ok := mq[fmt.Sprintf("%s:0", logFormat.Hostname)]
 | 
					 | 
				
			||||||
				if false == ok {
 | 
					 | 
				
			||||||
					// Create connection with conn_id = 0
 | 
					 | 
				
			||||||
					
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				olc.BindDN = &logFormat.BindDN
 | 
					 | 
				
			||||||
				op := &Operation{
 | 
					 | 
				
			||||||
					Time:           logFormat.Time,
 | 
					 | 
				
			||||||
					OpType:         logFormat.OpType,
 | 
					 | 
				
			||||||
					OpId:           &logFormat.OpId,
 | 
					 | 
				
			||||||
					BindDN:         logFormat.BindDN,
 | 
					 | 
				
			||||||
					BindMethod:     logFormat.BindMethod,
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				olc.Operations = append(olc.Operations, op)
 | 
					 | 
				
			||||||
				BindCntUnk.WithLabelValues(olc.Hostname).Inc()
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		mqMtx.Unlock()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
    2022-07-18T17:18:19.786218+02:00 ldap.domain.org slapd[82581] conn=16113 op=1 BIND dn="cn=coincoin,dc=domain,dc=org" mech=SIMPLE ssf=0
 | 
					 | 
				
			||||||
	*/
 | 
					 | 
				
			||||||
	if logFormat.BindDN != "" && logFormat.BindMech != "" {
 | 
					 | 
				
			||||||
		mqMtx.Lock()
 | 
					 | 
				
			||||||
		if olc, ok := mq[fmt.Sprintf("%s:%d", logFormat.Hostname, logFormat.ConnId)]; ok {
 | 
					 | 
				
			||||||
			for i := range olc.Operations {
 | 
					 | 
				
			||||||
				if *olc.Operations[i].OpId == logFormat.OpId {
 | 
					 | 
				
			||||||
					olc.Operations[i].BindMech = logFormat.BindMech
 | 
					 | 
				
			||||||
					olc.Operations[i].BindSSF = logFormat.BindSSF
 | 
					 | 
				
			||||||
					olc.Operations[i].SSF = logFormat.SSF
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		mqMtx.Unlock()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
		// Can be the result of many operation types
 | 
					 | 
				
			||||||
		2022-07-18T17:18:19.785681+02:00 ldap.domain.org slapd[82581] conn=16113 op=0 RESULT tag=97 err=0 text=
 | 
					 | 
				
			||||||
	*/
 | 
					 | 
				
			||||||
	if logFormat.Result == true {
 | 
					 | 
				
			||||||
		mqMtx.Lock()
 | 
					 | 
				
			||||||
		if olc, ok := mq[fmt.Sprintf("%s:%d", logFormat.Hostname, logFormat.ConnId)]; ok {
 | 
					 | 
				
			||||||
			for i := range olc.Operations {
 | 
					 | 
				
			||||||
				if olc.Operations[i].OpId != nil && *olc.Operations[i].OpId == logFormat.OpId {
 | 
					 | 
				
			||||||
					olc.Operations[i].ResTag = logFormat.ResTag
 | 
					 | 
				
			||||||
					olc.Operations[i].ResOid = logFormat.ResOid
 | 
					 | 
				
			||||||
					olc.Operations[i].ResErr = &logFormat.ResErr
 | 
					 | 
				
			||||||
					olc.Operations[i].ResQTime = logFormat.ResQTime
 | 
					 | 
				
			||||||
					olc.Operations[i].ResETime = logFormat.ResETime
 | 
					 | 
				
			||||||
					olc.Operations[i].ResText = logFormat.ResText
 | 
					 | 
				
			||||||
					
 | 
					 | 
				
			||||||
					// Dump to stdout if gFlatten...
 | 
					 | 
				
			||||||
					if gFlatten == true {
 | 
					 | 
				
			||||||
						var jsonBytes []byte
 | 
					 | 
				
			||||||
						if gFlatten {     
 | 
					 | 
				
			||||||
							jsonBytes, err = json.Marshal(OlcToFlat(olc)[i])
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
						if err != nil {
 | 
					 | 
				
			||||||
							log.Fatal(err)
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
						outfMtx.Lock()
 | 
					 | 
				
			||||||
						err = writeOut(string(jsonBytes), gOutputFile)
 | 
					 | 
				
			||||||
						outfMtx.Unlock()
 | 
					 | 
				
			||||||
						if err != nil {
 | 
					 | 
				
			||||||
							log.Fatal(err)
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
						// Then remove operation from OpenLDAPConnection so it wont output again
 | 
					 | 
				
			||||||
						olc.Operations = append(olc.Operations[:i], olc.Operations[i+1:]...)
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					break
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		mqMtx.Unlock()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
    2022-07-18T17:18:19.785881+02:00 ldap.domain.org slapd[82581] conn=16113 op=2 SRCH base="ou=users,dc=domain,dc=org" scope=2 deref=0 filter="(cn=pika)"
 | 
					 | 
				
			||||||
	*/
 | 
					 | 
				
			||||||
	if logFormat.SearchBase != "" {  
 | 
					 | 
				
			||||||
		mqMtx.Lock()
 | 
					 | 
				
			||||||
		if olc, ok := mq[fmt.Sprintf("%s:%d", logFormat.Hostname, logFormat.ConnId)]; ok {
 | 
					 | 
				
			||||||
			op := &Operation{
 | 
					 | 
				
			||||||
				Time:           logFormat.Time,
 | 
					 | 
				
			||||||
				OpType:         logFormat.OpType,
 | 
					 | 
				
			||||||
				OpId:           &logFormat.OpId,
 | 
					 | 
				
			||||||
				SearchBase:     logFormat.SearchBase,
 | 
					 | 
				
			||||||
				SearchScope:    logFormat.SearchScope,
 | 
					 | 
				
			||||||
				SearchDeref:    logFormat.SearchDeref,
 | 
					 | 
				
			||||||
				SearchFilter:   logFormat.SearchFilter,
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			olc.Operations = append(olc.Operations, op)
 | 
					 | 
				
			||||||
			SearchCnt.WithLabelValues(olc.Hostname).Inc()
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		mqMtx.Unlock()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
		2022-07-18T17:18:19.785897+02:00 ldap.domain.org slapd[82581] conn=16113 op=2 SRCH attr=dn
 | 
					 | 
				
			||||||
	*/
 | 
					 | 
				
			||||||
	if logFormat.SearchAttr != "" {
 | 
					 | 
				
			||||||
		mqMtx.Lock()
 | 
					 | 
				
			||||||
		if olc, ok := mq[fmt.Sprintf("%s:%d", logFormat.Hostname, logFormat.ConnId)]; ok {
 | 
					 | 
				
			||||||
			for i := range olc.Operations {
 | 
					 | 
				
			||||||
				if olc.Operations[i].OpId != nil && *olc.Operations[i].OpId == logFormat.OpId {
 | 
					 | 
				
			||||||
					olc.Operations[i].SearchAttr = logFormat.SearchAttr
 | 
					 | 
				
			||||||
					break
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		mqMtx.Unlock()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
		2022-07-18T17:18:19.785989+02:00 ldap.domain.org slapd[82581] conn=16113 op=2 SEARCH RESULT tag=101 err=0 nentries=1 text=
 | 
					 | 
				
			||||||
	*/
 | 
					 | 
				
			||||||
	if logFormat.SearchResult == true {
 | 
					 | 
				
			||||||
		mqMtx.Lock()
 | 
					 | 
				
			||||||
		if olc, ok := mq[fmt.Sprintf("%s:%d", logFormat.Hostname, logFormat.ConnId)]; ok {
 | 
					 | 
				
			||||||
			for i := range olc.Operations {
 | 
					 | 
				
			||||||
				if olc.Operations[i].OpId != nil && *olc.Operations[i].OpId == logFormat.OpId {
 | 
					 | 
				
			||||||
					olc.Operations[i].SearchResTag = logFormat.SearchResTag
 | 
					 | 
				
			||||||
					olc.Operations[i].SearchResErr = &logFormat.SearchResErr
 | 
					 | 
				
			||||||
					olc.Operations[i].SearchResQTime = logFormat.SearchResQTime
 | 
					 | 
				
			||||||
					olc.Operations[i].SearchResETime = logFormat.SearchResETime
 | 
					 | 
				
			||||||
					olc.Operations[i].SearchResNEntries = &logFormat.SearchResNEntries
 | 
					 | 
				
			||||||
					olc.Operations[i].SearchResText = logFormat.SearchResText
 | 
					 | 
				
			||||||
					
 | 
					 | 
				
			||||||
					// Dump to stdout if gFlatten...
 | 
					 | 
				
			||||||
					if gFlatten == true {
 | 
					 | 
				
			||||||
						var jsonBytes []byte
 | 
					 | 
				
			||||||
						if gFlatten {
 | 
					 | 
				
			||||||
							jsonBytes, err = json.Marshal(OlcToFlat(olc)[i])
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
						if err != nil {
 | 
					 | 
				
			||||||
							log.Fatal(err)
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
						outfMtx.Lock()
 | 
					 | 
				
			||||||
						err = writeOut(string(jsonBytes), gOutputFile)
 | 
					 | 
				
			||||||
						outfMtx.Unlock()
 | 
					 | 
				
			||||||
						if err != nil {
 | 
					 | 
				
			||||||
							log.Fatal(err)
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
						// Then remove operation from OpenLDAPConnection so it wont output again
 | 
					 | 
				
			||||||
						olc.Operations = append(olc.Operations[:i], olc.Operations[i+1:]...)
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					break
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		mqMtx.Unlock()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
		2022-07-18T14:35:17.381223+02:00 ldap.domain.org slapd slapd[82581] conn=16113 op=3 MOD dn="cn=coincoin,dc=domain,dc=org"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		If we don't have the initial connect, we will discard logs
 | 
					 | 
				
			||||||
	*/
 | 
					 | 
				
			||||||
	if logFormat.ModDN != "" {
 | 
					 | 
				
			||||||
		mqMtx.Lock()
 | 
					 | 
				
			||||||
		if olc, ok := mq[fmt.Sprintf("%s:%d", logFormat.Hostname, logFormat.ConnId)]; ok {
 | 
					 | 
				
			||||||
			op := &Operation{
 | 
					 | 
				
			||||||
				Time:           logFormat.Time,
 | 
					 | 
				
			||||||
				OpType:         logFormat.OpType,
 | 
					 | 
				
			||||||
				OpId:           &logFormat.OpId,
 | 
					 | 
				
			||||||
				ModDN:          logFormat.ModDN,
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			olc.Operations = append(olc.Operations, op)
 | 
					 | 
				
			||||||
			ModCnt.WithLabelValues(olc.Hostname).Inc()
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		mqMtx.Unlock()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
		2022-07-18T14:35:17.381233+02:00 ldap.domain.org slapd[82581] conn=16113 op=3 MOD attr=description
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		If we don't have the initial connect, we will discard logs
 | 
					 | 
				
			||||||
	*/
 | 
					 | 
				
			||||||
	if logFormat.ModAttr != "" {
 | 
					 | 
				
			||||||
		mqMtx.Lock()
 | 
					 | 
				
			||||||
		if olc, ok := mq[fmt.Sprintf("%s:%d", logFormat.Hostname, logFormat.ConnId)]; ok {
 | 
					 | 
				
			||||||
			for i := range olc.Operations {
 | 
					 | 
				
			||||||
				if olc.Operations[i].OpId != nil && *olc.Operations[i].OpId == logFormat.OpId {
 | 
					 | 
				
			||||||
					olc.Operations[i].ModAttr = logFormat.ModAttr
 | 
					 | 
				
			||||||
					break
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		mqMtx.Unlock()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
		2022-07-18T11:13:17.521717+02:00 ldap.domain.org slapd[82581] conn=16113 op=4 PASSMOD id="cn=pika,ou=users,dc=domain,dc=org" new
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		If we don't have the initial connect, we will discard logs
 | 
					 | 
				
			||||||
	*/
 | 
					 | 
				
			||||||
	if logFormat.PassModDN != "" {
 | 
					 | 
				
			||||||
		mqMtx.Lock()
 | 
					 | 
				
			||||||
		if olc, ok := mq[fmt.Sprintf("%s:%d", logFormat.Hostname, logFormat.ConnId)]; ok {
 | 
					 | 
				
			||||||
			op := &Operation{
 | 
					 | 
				
			||||||
				Time:           logFormat.Time,
 | 
					 | 
				
			||||||
				OpType:         logFormat.OpType,
 | 
					 | 
				
			||||||
				OpId:           &logFormat.OpId,
 | 
					 | 
				
			||||||
				PassModDN:      logFormat.PassModDN,
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			olc.Operations = append(olc.Operations, op)
 | 
					 | 
				
			||||||
			PassModCnt.WithLabelValues(olc.Hostname).Inc()
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		mqMtx.Unlock()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
		2022-07-18T17:18:19.785681+02:00 ldap.domain.org slapd[82581] conn=16113 op=8 UNBIND
 | 
					 | 
				
			||||||
	*/
 | 
					 | 
				
			||||||
	if logFormat.OpType == "unbind" {
 | 
					 | 
				
			||||||
		mqMtx.Lock()
 | 
					 | 
				
			||||||
    // unbind is a new operation
 | 
					 | 
				
			||||||
		if olc, ok := mq[fmt.Sprintf("%s:%d", logFormat.Hostname, logFormat.ConnId)]; ok {
 | 
					 | 
				
			||||||
			op := &Operation{
 | 
					 | 
				
			||||||
				Time:           logFormat.Time,
 | 
					 | 
				
			||||||
				OpType:         logFormat.OpType,
 | 
					 | 
				
			||||||
				OpId:           &logFormat.OpId,
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			olc.Operations = append(olc.Operations, op)
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			// Dump to stdout if gFlatten...
 | 
					 | 
				
			||||||
			if gFlatten == true {
 | 
					 | 
				
			||||||
				var jsonBytes []byte
 | 
					 | 
				
			||||||
				if gFlatten {
 | 
					 | 
				
			||||||
					jsonBytes, err = json.Marshal(OlcToFlat(olc)[len(olc.Operations)-1])
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					log.Fatal(err)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				outfMtx.Lock()
 | 
					 | 
				
			||||||
				err = writeOut(string(jsonBytes), gOutputFile)
 | 
					 | 
				
			||||||
				outfMtx.Unlock()
 | 
					 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					log.Fatal(err)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				// Then remove operation from OpenLDAPConnection so it wont output again
 | 
					 | 
				
			||||||
				olc.Operations = olc.Operations[:len(olc.Operations)-1]
 | 
					 | 
				
			||||||
				UnbindCnt.WithLabelValues(olc.Hostname).Inc()
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		mqMtx.Unlock()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
		2022-07-18T17:18:19.785681+02:00 ldap.domain.org slapd[82581] conn=16113 fd=34 closed
 | 
					 | 
				
			||||||
	*/
 | 
					 | 
				
			||||||
	// If gFlatten == false && We do not catch "closed", then the connection will never be displayed
 | 
					 | 
				
			||||||
	if logFormat.OpType == "close" {
 | 
					 | 
				
			||||||
		mqMtx.Lock()
 | 
					 | 
				
			||||||
		// close is a new operation with no op_id
 | 
					 | 
				
			||||||
		if olc, ok := mq[fmt.Sprintf("%s:%d", logFormat.Hostname, logFormat.ConnId)]; ok {
 | 
					 | 
				
			||||||
			op := &Operation{
 | 
					 | 
				
			||||||
				Time:           logFormat.Time,
 | 
					 | 
				
			||||||
				OpType:         logFormat.OpType,
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			olc.Operations = append(olc.Operations, op)
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			// Dump to stdout if gFlatten...
 | 
					 | 
				
			||||||
			if gFlatten == true {
 | 
					 | 
				
			||||||
				jsonBytes, err := json.Marshal(OlcToFlat(olc)[len(olc.Operations)-1])
 | 
					 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					log.Fatal(err)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				outfMtx.Lock()
 | 
					 | 
				
			||||||
				err = writeOut(string(jsonBytes), gOutputFile)
 | 
					 | 
				
			||||||
				outfMtx.Unlock()
 | 
					 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					log.Fatal(err)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				// Then remove operation from OpenLDAPConnection so it wont output again
 | 
					 | 
				
			||||||
				olc.Operations = olc.Operations[:len(olc.Operations)-1]
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				jsonBytes, err := json.Marshal(olc)
 | 
					 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					log.Fatal(err)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				outfMtx.Lock()
 | 
					 | 
				
			||||||
				err = writeOut(string(jsonBytes), gOutputFile)
 | 
					 | 
				
			||||||
				outfMtx.Unlock()
 | 
					 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					log.Fatal(err)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			CloseCnt.WithLabelValues(olc.Hostname).Inc()
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		mqMtx.Unlock()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func scanAndProcess(scanner *bufio.Scanner, isStdin bool, conn net.Conn, mQueue map[string]*OpenLdapConnection, 
 | 
					 | 
				
			||||||
	mqMtx *sync.Mutex, outfMtx *sync.Mutex, o *openldaplog.OpenldapLog) error {
 | 
					 | 
				
			||||||
	for {
 | 
					 | 
				
			||||||
		// If input is made via TCP Conn, we need to read from a connected net.Conn
 | 
					 | 
				
			||||||
		if scanner == nil || (isStdin == false && conn == nil) {
 | 
					 | 
				
			||||||
			return errors.New("Invalid input")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if false == scanner.Scan() {
 | 
					 | 
				
			||||||
			// After Scan returns false, the Err method will return any error that occurred during scanning, except that if it was io.EOF, Err will return nil
 | 
					 | 
				
			||||||
			if err := scanner.Err(); err != nil {
 | 
					 | 
				
			||||||
				log.Printf("Error reading data: %v\n", err.Error())
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if isStdin == false {
 | 
					 | 
				
			||||||
				log.Printf("No more data, closing connection.\n")
 | 
					 | 
				
			||||||
				// Should we?
 | 
					 | 
				
			||||||
				conn.Close()
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			// input is dead, abort mission!
 | 
					 | 
				
			||||||
			return errors.New("Read error")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		// Extend timeout after successful read (so we got an idle timeout)
 | 
					 | 
				
			||||||
		if isStdin == false && conn != nil {
 | 
					 | 
				
			||||||
			conn.SetReadDeadline(time.Now().Add(time.Duration(600) * time.Second))
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		LineReadCnt.Inc()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		read := scanner.Bytes()
 | 
					 | 
				
			||||||
		if gDebug {
 | 
					 | 
				
			||||||
			fmt.Printf("DEBUG: Received %v\n", string(read))
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		err := parseStoreAndWrite(read, mQueue, mqMtx, outfMtx, o)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			if err.Error() != "Error: Line do not match regex" {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				log.Printf("input do not match regex: %s\n", string(read))
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func processLogs(cmd *cobra.Command, args []string) {
 | 
					 | 
				
			||||||
	//var scanner *bufio.Scanner
 | 
					 | 
				
			||||||
	var listener net.Listener
 | 
					 | 
				
			||||||
	// Output file mutex
 | 
					 | 
				
			||||||
	var outfMtx sync.Mutex
 | 
					 | 
				
			||||||
	// mQueue mutex
 | 
					 | 
				
			||||||
	var mqMtx sync.Mutex
 | 
					 | 
				
			||||||
	var useStdin bool
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// create map of messages
 | 
					 | 
				
			||||||
	mQueue := make(map[string]*OpenLdapConnection)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	BuildInfo.WithLabelValues(Version, runtime.Version()).Set(1)
 | 
					 | 
				
			||||||
	StartTime.Set(float64(time.Now().Unix()))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Prometheus exporter
 | 
					 | 
				
			||||||
	if gPromListenAddress != "do-not-listen" {
 | 
					 | 
				
			||||||
		go func() {
 | 
					 | 
				
			||||||
			http.Handle(gPromMetricPath, promhttp.Handler())
 | 
					 | 
				
			||||||
			http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
 | 
					 | 
				
			||||||
				w.Write([]byte(`
 | 
					 | 
				
			||||||
				<html>
 | 
					 | 
				
			||||||
				<head><title>Openldap-log-parser Exporter</title></head>
 | 
					 | 
				
			||||||
				<body>
 | 
					 | 
				
			||||||
				<h1>Openldap-log-parser Exporter</h1>
 | 
					 | 
				
			||||||
				<p><a href='` + gPromMetricPath + `'>Metrics</a></p>
 | 
					 | 
				
			||||||
				</body>
 | 
					 | 
				
			||||||
				</html>`))
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
			log.Fatal(http.ListenAndServe(gPromListenAddress, nil))
 | 
					 | 
				
			||||||
		}()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Create PID file
 | 
					 | 
				
			||||||
	if len(gPidFilePath) > 0 {
 | 
					 | 
				
			||||||
		if pid, err := pidfile.Create(gPidFilePath); err != nil {
 | 
					 | 
				
			||||||
			log.Fatal(err)
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			defer pid.Clear()
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// initialize
 | 
					 | 
				
			||||||
	o := openldaplog.NewOpenldapLog(gDebug)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Get a writer, file or stdout
 | 
					 | 
				
			||||||
	_, File, err := NewWriter(gOutputFile)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		cmd.SetOutput(os.Stderr)
 | 
					 | 
				
			||||||
		cmd.Println(err)
 | 
					 | 
				
			||||||
		os.Exit(1)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Manage output file rotation when receiving SIGUSR1
 | 
					 | 
				
			||||||
	if len(gOutputFile) > 0 {
 | 
					 | 
				
			||||||
		sig := make(chan os.Signal)
 | 
					 | 
				
			||||||
		signal.Notify(sig, syscall.SIGUSR1)
 | 
					 | 
				
			||||||
		go func() {
 | 
					 | 
				
			||||||
			for {
 | 
					 | 
				
			||||||
				<-sig
 | 
					 | 
				
			||||||
				outfMtx.Lock()
 | 
					 | 
				
			||||||
				fmt.Println("SIGUSR1 received, recreating output file")
 | 
					 | 
				
			||||||
				File.Close()
 | 
					 | 
				
			||||||
				_, File, err = NewWriter(gOutputFile)
 | 
					 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					outfMtx.Unlock()
 | 
					 | 
				
			||||||
					cmd.SetOutput(os.Stderr)
 | 
					 | 
				
			||||||
					cmd.Println(err)
 | 
					 | 
				
			||||||
					os.Exit(1)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				outfMtx.Unlock()
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Cleaner thread
 | 
					 | 
				
			||||||
	//go periodicallyCleanMQueue(mQueue, &mqMtx)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Initialize Stdin input...
 | 
					 | 
				
			||||||
	if true == strings.EqualFold(gSyslogListenAddress, "do-not-listen") {
 | 
					 | 
				
			||||||
		useStdin = true
 | 
					 | 
				
			||||||
		scanner := bufio.NewScanner(os.Stdin)
 | 
					 | 
				
			||||||
		scanAndProcess(scanner, useStdin, nil, mQueue, &mqMtx, &outfMtx, o)
 | 
					 | 
				
			||||||
	// ...or manages incoming connections
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		if gDebug {
 | 
					 | 
				
			||||||
			fmt.Printf("DEBUG: Listening on %s\n", gSyslogListenAddress)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		listener, err = net.Listen("tcp", gSyslogListenAddress)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			log.Fatal(fmt.Sprintf("Error listening on %s: %v\n", gSyslogListenAddress, err))
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		for {
 | 
					 | 
				
			||||||
			connClt, err := listener.Accept()
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				log.Printf("Error accepting: %v", err)
 | 
					 | 
				
			||||||
				// Loop
 | 
					 | 
				
			||||||
				continue
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if gDebug {
 | 
					 | 
				
			||||||
				fmt.Printf("DEBUG: Accept connection from %s\n", connClt.RemoteAddr().String())
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			scanner := bufio.NewScanner(connClt)
 | 
					 | 
				
			||||||
			ConnectedClientCnt.Inc()
 | 
					 | 
				
			||||||
			go scanAndProcess(scanner, useStdin, connClt, mQueue, &mqMtx, &outfMtx, o)
 | 
					 | 
				
			||||||
			ConnectedClientCnt.Dec()
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if File != nil {
 | 
					 | 
				
			||||||
		outfMtx.Lock()
 | 
					 | 
				
			||||||
		File.Close()
 | 
					 | 
				
			||||||
		outfMtx.Unlock()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										5
									
								
								test/slapd-bind-no-accept.log
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								test/slapd-bind-no-accept.log
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					2022-07-18T09:25:35.224296+02:00 ldap.domain.org slapd[82581] conn=1512 op=1 BIND dn="cn=coincoin,dc=domain,dc=org" method=128
 | 
				
			||||||
 | 
					2022-07-18T09:25:35.224329+02:00 ldap.domain.org slapd[82581] conn=1512 op=1 BIND dn="cn=coincoin,dc=domain,dc=org" mech=SIMPLE ssf=0
 | 
				
			||||||
 | 
					2022-07-18T09:25:35.224353+02:00 ldap.domain.org slapd[82581] conn=1512 op=1 RESULT tag=97 err=0 text=
 | 
				
			||||||
 | 
					2022-07-18T09:25:35.225177+02:00 ldap.domain.org slapd[82581] conn=1512 op=2 UNBIND
 | 
				
			||||||
 | 
					2022-07-18T09:23:20.226352+02:00 ldap.domain.org slapd[82581] conn=1512 fd=10 closed
 | 
				
			||||||
		Reference in New Issue
	
	Block a user