diff --git a/internal/syslogparser/rfc3164/rfc3164.go b/internal/syslogparser/rfc3164/rfc3164.go index 6382104..ae736da 100644 --- a/internal/syslogparser/rfc3164/rfc3164.go +++ b/internal/syslogparser/rfc3164/rfc3164.go @@ -144,6 +144,10 @@ const FortiOSTimestampRePattern = `eventtime=(\d+)` var fortiOSTimestampCaptureRe = regexp.MustCompile(FortiOSTimestampRePattern) +const ciscoASATimestampCapture = `^<\d+>(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z)` + +var ciscoASATimestampRegexp = regexp.MustCompile(ciscoASATimestampCapture) + func (p *Parser) parseFortiOSHeader() (header, error) { //FortiOS log, do a regex parse because it is not standard //example log : <133>date=2024-01-31 time=13:36:54 devname="Y21FS1-101F" devid="FGUSI@#J%JI@I" eventtime=1706726214463347261 tz="-0500" logid="0000000011" type="traffic" subtype="forward" level="notice" vd="root" srcip=10.2.2.30 srcport=50295 srcintf="almi-f5s" srcintfrole="undefined" dstip=10.3.1.1 dstport=90 dstintf="sr929" dstintfrole="lan" srccountry="Reserved" dstcountry="Reserved" sessionid=1583922 proto=3 action="start" policyid=905 policytype="policy" poluuid="fjkdsljjlk-5u39582305-573289527358" policyname="FIREWALL_POLICY" user="USER_ADMIN" authserver="AGENT_FO" dstuser="SVC_USER" centralnatid=5 service="TESTSERV" trandisp="noop" duration=0 sentbyte=0 rcvdbyte=0 sentpkt=0 rcvdpkt=0 vpntype="ipsecvpn" appcat="unscanned" @@ -170,6 +174,21 @@ func (p *Parser) parseFortiOSHeader() (header, error) { }, nil } +func (p *Parser) parseCiscoASA_RFC5424() (header, error) { + //example log : <166>2018-06-27T12:17:46Z asa : %ASA-6-110002: Failed to locate egress interface for protocol from src interface :src IP/src port to dest IP/dest port + match := ciscoASATimestampRegexp.FindStringSubmatch(string(p.buff)) + if match != nil && len(match) > 1 { + timestampStr := match[1] + parsedTime, err := time.Parse(time.RFC3339, timestampStr) + if err != nil { + return header{}, fmt.Errorf("failed to parse cisco ASA RFC5424 timestamp: %v", err) + } + fixTimestampIfNeeded(&parsedTime) + return header{timestamp: parsedTime, hostname: ""}, nil + } + return header{}, fmt.Errorf("failed to parse cisco ASA RFC5424 timestamp: %v", "no match") +} + func (p *Parser) Parse() error { tcursor := p.cursor p.message = rfc3164message{content: string(p.buff)} @@ -227,6 +246,14 @@ func (p *Parser) Parse() error { setDefaultFail() return err } + } else if errors.Is(err, syslogparser.ErrCiscoASARFC5424) { + skipMessageParse = true + + hdr, err = p.parseCiscoASA_RFC5424() + if err != nil { + setDefaultFail() + return err + } } else { setDefaultFail() @@ -311,6 +338,18 @@ func (p *Parser) parsemessage() (rfc3164message, error) { return msg, err } +const ciscoASA_RFC5424Format = `^<\d+>(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z)` + +var ciscoASA_RFC5424Regexp = regexp.MustCompile(ciscoASA_RFC5424Format) + +func checkCiscoASA_RFC5424(buff []byte) bool { + if ciscoASA_RFC5424Regexp.MatchString(string(buff)) { + return true + } + + return false +} + const ciscoASAPriorityFormat = `<\d+>:` var ciscoASARegexp = regexp.MustCompile(ciscoASAPriorityFormat) @@ -377,6 +416,10 @@ func (p *Parser) parseTimestamp() (time.Time, error) { return ts, syslogparser.ErrFortiOSFormat } + if checkCiscoASA_RFC5424(p.buff) { + return ts, syslogparser.ErrCiscoASARFC5424 + } + return ts, fmt.Errorf("%v %s", syslogparser.ErrTimestampUnknownFormat, string(p.buff)) } diff --git a/internal/syslogparser/rfc3164/rfc3164_test.go b/internal/syslogparser/rfc3164/rfc3164_test.go index cb609af..52f0277 100644 --- a/internal/syslogparser/rfc3164/rfc3164_test.go +++ b/internal/syslogparser/rfc3164/rfc3164_test.go @@ -23,6 +23,37 @@ var ( lastTriedTimestampLen = 15 ) +func (s *Rfc3164TestSuite) TestParserCiscoASA_Valid_RFC5424(c *C) { + buff := []byte(`<166>2018-06-27T12:17:46Z asa : %ASA-6-110002: Failed to locate egress interface for protocol from src interface :src IP/src port to dest IP/dest port`) + + p := NewParser(buff) + expectedP := &Parser{ + buff: buff, + cursor: 0, + l: len(buff), + location: time.UTC, + } + + c.Assert(p, DeepEquals, expectedP) + + err := p.Parse() + c.Assert(err, IsNil) + + obtained := p.Dump() + + expected := syslogparser.LogParts{ + "timestamp": time.Date(2018, time.June, 27, 12, 17, 46, 0, time.UTC), + "hostname": "", + "tag": "", + "content": `<166>2018-06-27T12:17:46Z asa : %ASA-6-110002: Failed to locate egress interface for protocol from src interface :src IP/src port to dest IP/dest port`, + "priority": 166, + "facility": 20, + "severity": 6, + } + + c.Assert(obtained, DeepEquals, expected) +} + func (s *Rfc3164TestSuite) TestParserCiscoASA_NoTimestamp(c *C) { buff := []byte(`<34>:%ASA-session-6-106100: access-list outside_access_in permitted tcp outside/155.138.247.97(58344) -> NEX-DMZ/10.90.3.239(443) hit-cnt 1 first hit [0x8fca8d4d, 0xf3808cf3]`) diff --git a/internal/syslogparser/rfc5424/rfc5424.go b/internal/syslogparser/rfc5424/rfc5424.go index aa1ef8a..8dd75dc 100644 --- a/internal/syslogparser/rfc5424/rfc5424.go +++ b/internal/syslogparser/rfc5424/rfc5424.go @@ -4,10 +4,8 @@ package rfc5424 import ( - "errors" "fmt" "math" - "regexp" "strconv" "time" @@ -32,7 +30,6 @@ var ( ErrInvalidProcId = &syslogparser.ParserError{"Invalid proc ID"} ErrInvalidMsgId = &syslogparser.ParserError{"Invalid msg ID"} ErrNoStructuredData = &syslogparser.ParserError{"No structured data"} - ErrCiscoASARFC5424 = &syslogparser.ParserError{"Cisco ASA RFC5424"} ) type Parser struct { @@ -91,12 +88,6 @@ func (p *Parser) Parse() error { hdr, err := p.parseHeader() if err != nil { - if errors.Is(err, ErrCiscoASARFC5424) { - p.header = hdr - p.structuredData = "-" - p.header.version = 1 - return nil - } return err } @@ -155,10 +146,6 @@ func (p *Parser) parseHeader() (header, error) { ts, err := p.parseTimestamp() if err != nil { - if errors.Is(err, ErrCiscoASARFC5424) { - hdr.timestamp = ts - return hdr, ErrCiscoASARFC5424 - } return hdr, err } @@ -274,10 +261,6 @@ func parseUnixTimestamp(buff []byte, cursor *int, l int) (time.Time, error) { return time.Unix(sec, nsec), nil } -const ciscoASATimestampCapture = `^<\d+>(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z)` - -var ciscoASATimestampRegexp = regexp.MustCompile(ciscoASATimestampCapture) - // https://tools.ietf.org/html/rfc5424#section-6.2.3 func (p *Parser) parseTimestamp() (time.Time, error) { ts := time.Now().UTC() @@ -303,19 +286,7 @@ func (p *Parser) parseTimestamp() (time.Time, error) { fd, err := parseFullDate(p.buff, &p.cursor, p.l) if err != nil { - if errors.Is(err, ErrCiscoASARFC5424) { - match := ciscoASATimestampRegexp.FindStringSubmatch(string(p.buff)) - if match != nil && len(match) > 1 { - timestampStr := match[1] - parsedTime, err := time.Parse(time.RFC3339, timestampStr) - if err != nil { - return ts, fmt.Errorf("failed to parse cisco ASA RFC5424 timestamp: %v", err) - } - return parsedTime, ErrCiscoASARFC5424 - } - } else { - return ts, err - } + return ts, err } if p.cursor >= p.l || p.buff[p.cursor] != 'T' { @@ -418,18 +389,6 @@ func parseFullDate(buff []byte, cursor *int, l int) (fullDate, error) { return fd, nil } -const ciscoASA_RFC5424Format = `^<\d+>(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z)` - -var ciscoASA_RFC5424Regexp = regexp.MustCompile(ciscoASA_RFC5424Format) - -func checkCiscoASA_RFC5424(buff []byte) bool { - if ciscoASA_RFC5424Regexp.MatchString(string(buff)) { - return true - } - - return false -} - // DATE-FULLYEAR = 4DIGIT func parseYear(buff []byte, cursor *int, l int) (int, error) { yearLen := 4 @@ -446,9 +405,6 @@ func parseYear(buff []byte, cursor *int, l int) (int, error) { year, err := strconv.Atoi(sub) if err != nil { - if checkCiscoASA_RFC5424(buff) { - return 0, ErrCiscoASARFC5424 - } return 0, ErrYearInvalid } diff --git a/internal/syslogparser/rfc5424/rfc5424_test.go b/internal/syslogparser/rfc5424/rfc5424_test.go index feb2c65..a593360 100644 --- a/internal/syslogparser/rfc5424/rfc5424_test.go +++ b/internal/syslogparser/rfc5424/rfc5424_test.go @@ -17,40 +17,6 @@ type Rfc5424TestSuite struct { var _ = Suite(&Rfc5424TestSuite{}) -func (s *Rfc5424TestSuite) TestParserCiscoASA_Valid_RFC5424(c *C) { - buff := []byte(`<166>2018-06-27T12:17:46Z asa : %ASA-6-110002: Failed to locate egress interface for protocol from src interface :src IP/src port to dest IP/dest port`) - - p := NewParser(buff) - expectedP := &Parser{ - buff: buff, - cursor: 0, - l: len(buff), - } - - c.Assert(p, DeepEquals, expectedP) - - err := p.Parse() - c.Assert(err, IsNil) - - obtained := p.Dump() - - expected := syslogparser.LogParts{ - "priority": 166, - "facility": 20, - "severity": 6, - "version": 1, - "timestamp": time.Date(2018, time.June, 27, 12, 17, 46, 0, time.UTC), - "hostname": "", - "app_name": "", - "proc_id": "", - "msg_id": "", - "structured_data": "-", - "message": "<166>2018-06-27T12:17:46Z asa : %ASA-6-110002: Failed to locate egress interface for protocol from src interface :src IP/src port to dest IP/dest port", - } - - c.Assert(obtained, DeepEquals, expected) -} - func (s *Rfc5424TestSuite) TestParser_Valid(c *C) { fixtures := []string{ // no STRUCTURED-DATA diff --git a/internal/syslogparser/syslogparser.go b/internal/syslogparser/syslogparser.go index 8aeb5f7..dd5e713 100644 --- a/internal/syslogparser/syslogparser.go +++ b/internal/syslogparser/syslogparser.go @@ -31,6 +31,7 @@ var ( ErrSonicOSFormat = &ParserError{"SonicOS Format"} ErrFortiOSFormat = &ParserError{"FortiOS Format"} ErrCiscoASAFormat = &ParserError{"Cisco ASA Format"} + ErrCiscoASARFC5424 = &ParserError{"Cisco ASA RFC5424"} ErrHostnameTooShort = &ParserError{"Hostname field too short"} )