@@ -2,6 +2,7 @@ package service
22
33import (
44 "fmt"
5+ "net"
56 "os"
67 "os/user"
78 "path"
@@ -291,7 +292,7 @@ func (u *SSHService) LoadLog(c *gin.Context, req dto.SearchSSHLog) (*dto.SSHLog,
291292 if err != nil {
292293 return err
293294 }
294- if ! info .IsDir () && (strings .HasPrefix (info .Name (), "secure" ) || strings .HasPrefix (info .Name (), "auth" )) {
295+ if ! info .IsDir () && (strings .HasPrefix (info .Name (), "secure" ) || strings .HasPrefix (info .Name (), "auth" ) || strings . HasPrefix ( info . Name (), "messages" ) ) {
295296 if ! strings .HasSuffix (info .Name (), ".gz" ) {
296297 fileList = append (fileList , sshFileItem {Name : pathItem , Year : info .ModTime ().Year ()})
297298 return nil
@@ -319,7 +320,8 @@ func (u *SSHService) LoadLog(c *gin.Context, req dto.SearchSSHLog) (*dto.SSHLog,
319320 nyc , _ := time .LoadLocation (common .LoadTimeZoneByCmd ())
320321 for _ , file := range fileList {
321322 commandItem := ""
322- if strings .HasPrefix (path .Base (file .Name ), "secure" ) {
323+ switch {
324+ case strings .HasPrefix (path .Base (file .Name ), "secure" ):
323325 switch req .Status {
324326 case constant .StatusSuccess :
325327 commandItem = fmt .Sprintf ("cat %s | grep -a Accepted %s" , file .Name , command )
@@ -328,8 +330,16 @@ func (u *SSHService) LoadLog(c *gin.Context, req dto.SearchSSHLog) (*dto.SSHLog,
328330 default :
329331 commandItem = fmt .Sprintf ("cat %s | grep -aE '(Failed password for|Accepted)' %s" , file .Name , command )
330332 }
331- }
332- if strings .HasPrefix (path .Base (file .Name ), "auth.log" ) {
333+ case strings .HasPrefix (path .Base (file .Name ), "messages" ):
334+ switch req .Status {
335+ case constant .StatusSuccess :
336+ commandItem = fmt .Sprintf ("cat %s | grep -aE 'sshd.*Accepted (password|publickey)' %s" , file .Name , command )
337+ case constant .StatusFailed :
338+ commandItem = fmt .Sprintf ("cat %s | grep -aE 'sshd.*(Failed password for|Connection closed by authenticating user)' %s" , file .Name , command )
339+ default :
340+ commandItem = fmt .Sprintf ("cat %s | grep -aE 'sshd.*(Accepted|Failed password for|Connection closed)' %s" , file .Name , command )
341+ }
342+ case strings .HasPrefix (path .Base (file .Name ), "auth.log" ):
333343 switch req .Status {
334344 case constant .StatusSuccess :
335345 commandItem = fmt .Sprintf ("cat %s | grep -a Accepted %s" , file .Name , command )
@@ -425,107 +435,160 @@ func loadSSHData(c *gin.Context, command string, showCountFrom, showCountTo, cur
425435 lines := strings .Split (string (stdout2 ), "\n " )
426436 for i := len (lines ) - 1 ; i >= 0 ; i -- {
427437 var itemData dto.SSHHistory
428- switch {
429- case strings .Contains (lines [i ], "Failed password for" ):
430- itemData = loadFailedSecureDatas (lines [i ])
431- if len (itemData .Address ) != 0 {
432- if successCount + failedCount >= showCountFrom && successCount + failedCount < showCountTo {
433- itemData .Area , _ = geo .GetIPLocation (itemData .Address , common .GetLang (c ))
434- itemData .Date = loadDate (currentYear , itemData .DateStr , nyc )
435- datas = append (datas , itemData )
436- }
437- failedCount ++
438- }
439- case strings .Contains (lines [i ], "Connection closed by authenticating user" ):
440- itemData = loadFailedAuthDatas (lines [i ])
441- if len (itemData .Address ) != 0 {
442- if successCount + failedCount >= showCountFrom && successCount + failedCount < showCountTo {
443- itemData .Area , _ = geo .GetIPLocation (itemData .Address , common .GetLang (c ))
444- itemData .Date = loadDate (currentYear , itemData .DateStr , nyc )
445- datas = append (datas , itemData )
446- }
447- failedCount ++
438+ line := strings .TrimSpace (lines [i ])
439+ if line == "" {
440+ continue
441+ }
442+
443+ parts := strings .Fields (line )
444+ if len (parts ) < 12 {
445+ continue
446+ }
447+
448+ // 统一时间解析逻辑
449+ var dateStr string
450+ var timeIndex int
451+ if strings .Contains (parts [0 ], "-" ) { // 处理RFC3339时间格式
452+ t , err := time .Parse (time .RFC3339Nano , parts [0 ])
453+ if err == nil {
454+ dateStr = t .Format ("2006 Jan 2 15:04:05" )
455+ timeIndex = 0
448456 }
449- case strings .Contains (lines [i ], "Accepted " ):
450- itemData = loadSuccessDatas (lines [i ])
451- if len (itemData .Address ) != 0 {
452- if successCount + failedCount >= showCountFrom && successCount + failedCount < showCountTo {
453- itemData .Area , _ = geo .GetIPLocation (itemData .Address , common .GetLang (c ))
454- itemData .Date = loadDate (currentYear , itemData .DateStr , nyc )
455- datas = append (datas , itemData )
456- }
457- successCount ++
457+ } else { // 处理系统日志格式
458+ dateParts := parts [:3 ]
459+ if len (dateParts ) < 3 {
460+ continue
458461 }
462+ dateStr = strings .Join (dateParts , " " )
463+ timeIndex = 3
464+ }
465+
466+ // 根据日志类型解析内容
467+ switch {
468+ case strings .Contains (line , "Failed password for" ):
469+ itemData = parseFailedPasswordLog (parts , timeIndex , dateStr )
470+ case strings .Contains (line , "Connection closed by authenticating user" ):
471+ itemData = parseConnectionClosedLog (parts , timeIndex , dateStr )
472+ case strings .Contains (line , "Accepted" ):
473+ itemData = parseAcceptedLog (parts , timeIndex , dateStr )
474+ default :
475+ continue
476+ }
477+ if itemData .Address == "" {
478+ continue
479+ }
480+
481+ total := successCount + failedCount
482+ if total >= showCountFrom && total < showCountTo {
483+ itemData .Area , _ = geo .GetIPLocation (itemData .Address , common .GetLang (c ))
484+ itemData .Date = parseLogDate (currentYear , dateStr , nyc )
485+ datas = append (datas , itemData )
486+ }
487+
488+ if itemData .Status == constant .StatusSuccess {
489+ successCount ++
490+ } else {
491+ failedCount ++
492+ }
493+
494+ if total >= showCountTo {
495+ break
459496 }
460497 }
461498 return datas , successCount , failedCount
462499}
463500
464- func loadSuccessDatas (line string ) dto.SSHHistory {
465- var data dto.SSHHistory
466- parts := strings .Fields (line )
467- index , dataStr := analyzeDateStr (parts )
468- if dataStr == "" {
469- return data
470- }
471- data .DateStr = dataStr
472- data .AuthMode = parts [4 + index ]
473- data .User = parts [6 + index ]
474- data .Address = parts [8 + index ]
475- data .Port = parts [10 + index ]
476- data .Status = constant .StatusSuccess
501+ func parseFailedPasswordLog (parts []string , timeIndex int , dateStr string ) dto.SSHHistory {
502+ data := dto.SSHHistory {
503+ DateStr : dateStr ,
504+ Status : constant .StatusFailed ,
505+ AuthMode : "publickey" ,
506+ }
507+
508+ // 查找关键字段位置
509+ for i := timeIndex ; i < len (parts ); i ++ {
510+ switch parts [i ] {
511+ case "for" :
512+ if i + 1 < len (parts ) {
513+ data .User = parts [i + 1 ]
514+ }
515+ case "from" :
516+ if i + 1 < len (parts ) {
517+ data .Address = parts [i + 1 ]
518+ }
519+ case "port" :
520+ if i + 1 < len (parts ) {
521+ data .Port = parts [i + 1 ]
522+ }
523+ case "password" :
524+ data .AuthMode = "password"
525+ }
526+ }
527+
528+ if strings .Contains (strings .Join (parts , " " ), "invalid user" ) {
529+ data .Message = "invalid user attempt"
530+ }
477531 return data
478532}
479533
480- func loadFailedAuthDatas (line string ) dto.SSHHistory {
481- var data dto.SSHHistory
482- parts := strings .Fields (line )
483- index , dataStr := analyzeDateStr (parts )
484- if dataStr == "" {
485- return data
486- }
487- data .DateStr = dataStr
488- switch index {
489- case 1 :
490- data .User = parts [9 ]
491- case 2 :
492- data .User = parts [10 ]
493- default :
494- data .User = parts [7 ]
495- }
496- data .AuthMode = parts [6 + index ]
497- data .Address = parts [9 + index ]
498- data .Port = parts [11 + index ]
499- data .Status = constant .StatusFailed
500- if strings .Contains (line , ": " ) {
501- data .Message = strings .Split (line , ": " )[1 ]
534+ func parseConnectionClosedLog (parts []string , timeIndex int , dateStr string ) dto.SSHHistory {
535+ data := dto.SSHHistory {
536+ DateStr : dateStr ,
537+ Status : constant .StatusFailed ,
538+ }
539+
540+ // 解析Alpine格式的特殊字段
541+ fieldStart := timeIndex + 5 // 跳过时间、主机、进程字段
542+ for i := fieldStart ; i < len (parts ); i ++ {
543+ switch {
544+ case parts [i ] == "user" :
545+ if i + 1 < len (parts ) {
546+ data .User = parts [i + 1 ]
547+ }
548+ case parts [i ] == "port" :
549+ if i + 1 < len (parts ) {
550+ data .Port = parts [i + 1 ]
551+ }
552+ case isIPAddress (parts [i ]):
553+ data .Address = parts [i ]
554+ }
502555 }
503556 return data
504557}
505- func loadFailedSecureDatas (line string ) dto.SSHHistory {
506- var data dto.SSHHistory
507- parts := strings .Fields (line )
508- index , dataStr := analyzeDateStr (parts )
509- if dataStr == "" {
510- return data
511- }
512- data .DateStr = dataStr
513- if strings .Contains (line , " invalid " ) {
514- data .AuthMode = parts [4 + index ]
515- index += 2
516- } else {
517- data .AuthMode = parts [4 + index ]
558+
559+ func parseAcceptedLog (parts []string , timeIndex int , dateStr string ) dto.SSHHistory {
560+ data := dto.SSHHistory {
561+ DateStr : dateStr ,
562+ Status : constant .StatusSuccess ,
563+ AuthMode : "password" , // 默认值
518564 }
519- data .User = parts [6 + index ]
520- data .Address = parts [8 + index ]
521- data .Port = parts [10 + index ]
522- data .Status = constant .StatusFailed
523- if strings .Contains (line , ": " ) {
524- data .Message = strings .Split (line , ": " )[1 ]
565+
566+ // 处理不同日志格式
567+ fieldStart := timeIndex + 5 // 基础字段偏移
568+ for i := fieldStart ; i < len (parts ); i ++ {
569+ switch {
570+ case parts [i ] == "for" :
571+ if i + 1 < len (parts ) {
572+ data .User = parts [i + 1 ]
573+ }
574+ case parts [i ] == "from" :
575+ if i + 1 < len (parts ) {
576+ data .Address = parts [i + 1 ]
577+ }
578+ case parts [i ] == "port" :
579+ if i + 1 < len (parts ) {
580+ data .Port = parts [i + 1 ]
581+ }
582+ case strings .Contains (parts [i ], "ssh2:" ):
583+ data .AuthMode = "publickey"
584+ case parts [i ] == "publickey" :
585+ data .AuthMode = "publickey"
586+ case parts [i ] == "password" :
587+ data .AuthMode = "password"
588+ }
525589 }
526590 return data
527591}
528-
529592func handleGunzip (path string ) error {
530593 if _ , err := cmd .Execf ("gunzip %s" , path ); err != nil {
531594 return err
@@ -544,32 +607,26 @@ func loadServiceName() (string, error) {
544607 return serviceName , nil
545608}
546609
547- func loadDate (currentYear int , DateStr string , nyc * time.Location ) time.Time {
548- itemDate , err := time .ParseInLocation ("2006 Jan 2 15:04:05" , fmt .Sprintf ("%d %s" , currentYear , DateStr ), nyc )
549- if err != nil {
550- itemDate , _ = time .ParseInLocation ("2006 Jan 2 15:04:05" , DateStr , nyc )
610+ func parseLogDate (currentYear int , dateStr string , loc * time.Location ) time.Time {
611+ // 处理带年份和不带年份的情况
612+ formats := []string {
613+ "2006 Jan 2 15:04:05" ,
614+ "Jan 2 15:04:05" ,
551615 }
552- return itemDate
553- }
554616
555- func analyzeDateStr (parts []string ) (int , string ) {
556- t , err := time .Parse (time .RFC3339Nano , parts [0 ])
557- if err == nil {
558- if len (parts ) < 12 {
559- return 0 , ""
560- }
561- return 0 , t .Format ("2006 Jan 2 15:04:05" )
562- }
563- t , err = time .Parse (constant .DateTimeLayout , fmt .Sprintf ("%s %s" , parts [0 ], parts [1 ]))
564- if err == nil {
565- if len (parts ) < 14 {
566- return 0 , ""
617+ for _ , format := range formats {
618+ t , err := time .ParseInLocation (format , dateStr , loc )
619+ if err == nil {
620+ if t .Year () == 0 {
621+ return time .Date (currentYear , t .Month (), t .Day (), t .Hour (), t .Minute (), t .Second (), 0 , loc )
622+ }
623+ return t
567624 }
568- return 1 , t .Format ("2006 Jan 2 15:04:05" )
569625 }
626+ return time .Now ().In (loc )
627+ }
628+
629+ func isIPAddress (s string ) bool {
630+ return net .ParseIP (s ) != nil
570631
571- if len (parts ) < 14 {
572- return 0 , ""
573- }
574- return 2 , fmt .Sprintf ("%s %s %s" , parts [0 ], parts [1 ], parts [2 ])
575632}
0 commit comments