diff --git a/openldap-log-parser.go b/openldap-log-parser.go index a22571e..e3f90da 100644 --- a/openldap-log-parser.go +++ b/openldap-log-parser.go @@ -47,10 +47,12 @@ const ( AddDnRE = `(?:ADD dn="(.*)")?` // group[42] DelDnRE = `(?:DEL dn="(.*)")?` - + // group[43], 44, 45, 46 + MetaBackOpErrorRE = `(?:meta_back_search\[([0-9]+)\] match="(.*)" err=([0-9]+) \(Operations error\) text="([^\"]*)")?` + LogLineRE = SyslogPri + TimeRE + ` ` + HostRE + ` ` + ProcessRE + ` ` + ConnIdRE + ` ` + ConnFdRE + OperationIdRE + ` ` + AcceptRE + STARTTLSRE + BindMethodRE + BindMechRE + ResultRE + SearchBaseRE + SearchAttrRE + SearchResultRE + ModDnRE + ModAttrRE + - PassModRE + UnbindRE + ConnClosedRE + AddDnRE + DelDnRE + PassModRE + UnbindRE + ConnClosedRE + AddDnRE + DelDnRE + MetaBackOpErrorRE ) type ( @@ -58,7 +60,7 @@ type ( LogFormat LogFormat Regexp *regexp.Regexp } - + LogFormat struct { Time *time.Time `json:"time"` Hostname string `json:"hostname"` @@ -100,6 +102,10 @@ type ( SearchResETime string `json:"search_res_etime"` SearchResNEntries int `json:"search_res_nentries"` SearchResText string `json:"search_res_text"` + ErrorBackend int `json:"err_backend"` + ErrorMatch string `json:"err_match"` + ErrorErr int `json:"err_error"` + ErrorText string `json:"err_text"` } ) @@ -150,7 +156,12 @@ func (o *OpenldapLog) Parse(text []byte) (LogFormat, error) { } serr,_ := strconv.Atoi(string(group[31])) srentries, _ := strconv.Atoi(string(group[34])) - + + // 0 even if no error, so don't use this for error testing + backend, _ := strconv.Atoi(string(group[43])) + // 0 if no error, so use this instead + backerr, _ := strconv.Atoi(string(group[45])) + logFormat := LogFormat{ Time: &t, Hostname: string(group[2]), @@ -192,6 +203,10 @@ func (o *OpenldapLog) Parse(text []byte) (LogFormat, error) { PassModDN: string(group[38]), AddDN: string(group[41]), DelDN: string(group[42]), + ErrorBackend: backend, + ErrorMatch: string(group[44]), + ErrorErr: backerr, + ErrorText: string(group[46]), } // Now handle Operation Type @@ -215,6 +230,8 @@ func (o *OpenldapLog) Parse(text []byte) (LogFormat, error) { logFormat.OpType = "add" } else if len(group[42]) > 0 { logFormat.OpType = "del" + } else if logFormat.ErrorErr != 0 { + logFormat.OpType = "error" } return logFormat, nil diff --git a/openldap-log-parser/cmd/root.go b/openldap-log-parser/cmd/root.go index 5ffd957..f1194ef 100644 --- a/openldap-log-parser/cmd/root.go +++ b/openldap-log-parser/cmd/root.go @@ -64,10 +64,10 @@ type ( 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"` + ResErr *int `json:"result_err,omitempty"` ResQTime string `json:"result_qtime,omitempty"` ResETime string `json:"result_etime,omitempty"` - ResNEntries *int `json:"result_nentries,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"` @@ -75,11 +75,15 @@ type ( 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"` + 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"` + SearchResNEntries *int `json:"search_res_nentries,omitempty"` SearchResText string `json:"search_res_text,omitempty"` + ErrorBackend *int `json:"err_backend,omitempty"` + ErrorMatch string `json:"err_match,omitempty"` + ErrorErr *int `json:"err_error,omitempty"` + ErrorText string `json:"err_text,omitempty"` } OpenLdapConnectionFlat struct { @@ -122,6 +126,10 @@ type ( SearchResETime string `json:"search_res_etime,omitempty"` SearchResNEntries *int `json:"search_res_nentries,omitempty"` SearchResText string `json:"search_res_text,omitempty"` + ErrorBackend *int `json:"err_backend,omitempty"` + ErrorMatch string `json:"err_match,omitempty"` + ErrorErr *int `json:"err_error,omitempty"` + ErrorText string `json:"err_text,omitempty"` } ) @@ -129,7 +137,7 @@ var ( File os.File Writer *bufio.Writer - Version = "0.6.13" + Version = "0.6.14" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "openldaplogparser_build_info", @@ -195,6 +203,10 @@ var ( Name: "openldaplogparser_starttlscount", Help: "Number of STARTTLS commands executed", }, []string{"host"}) + ErrorCnt = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "openldaplogparser_error_count", + Help: "Number of errors", + }, []string{"host","type"}) rootCmd = &cobra.Command{ Use: "openldap-log-parser", @@ -309,6 +321,11 @@ func OlcToFlat(olc *OpenLdapConnection) []OpenLdapConnectionFlat { olcf[i].ResQTime = olc.Operations[i].ResQTime olcf[i].ResETime = olc.Operations[i].ResETime olcf[i].ResText = olc.Operations[i].ResText + case "error": + olcf[i].ErrorBackend = olc.Operations[i].ErrorBackend + olcf[i].ErrorMatch = olc.Operations[i].ErrorMatch + olcf[i].ErrorErr = olc.Operations[i].ErrorErr + olcf[i].ErrorText = olc.Operations[i].ErrorText } } @@ -910,10 +927,66 @@ func parseStoreAndWrite(input []byte, mq map[string]*OpenLdapConnection, mqMtx * } // Then remove operation from OpenLDAPConnection so it wont output again olc.Operations = olc.Operations[:len(olc.Operations)-1] - UnbindCnt.WithLabelValues(olc.Hostname).Inc() } + UnbindCnt.WithLabelValues(olc.Hostname).Inc() } mqMtx.Unlock() + return nil + } + + /* 2022-07-18T09:25:35.224779+02:00 ldap.domain.org slapd[82581] conn=1512 op=2 meta_back_search[0] match="" err=1 (Operations error) text="000004DC: LdapErr: DSID-0C090BA8, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, v3839".*/ + if logFormat.ErrorErr != 0 { + op := &Operation{ + Time: logFormat.Time, + OpType: logFormat.OpType, + OpId: &logFormat.OpId, + ErrorBackend: &logFormat.ErrorBackend, + ErrorMatch: logFormat.ErrorMatch, + ErrorErr: &logFormat.ErrorErr, + ErrorText: logFormat.ErrorText, + } + mqMtx.Lock() + // Do this connection exists ? + olc, ok := mq[fmt.Sprintf("%s:%d", logFormat.Hostname, logFormat.ConnId)] + if false == ok { + if false == gDispUnkConn { + mqMtx.Unlock() + return nil + } else { + // Create connection + 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, + } + mq[fmt.Sprintf("%s:%d", logFormat.Hostname, logFormat.ConnId)] = olc + } + } + olc.Operations = append(olc.Operations, op) + // Dump to stdout if gFlatten, b/c this is a standalone event + 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] + } + mqMtx.Unlock() + ErrorCnt.WithLabelValues(olc.Hostname, "meta_back_search").Inc() + return nil } /*