Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 74 additions & 22 deletions agent/app/service/alert_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package service

import (
"encoding/json"
"errors"
"fmt"
"math"
"net"
Expand Down Expand Up @@ -32,6 +33,7 @@ const (
ResourceAlertInterval = 30
CheckIntervalSec = 3
LoadCheckIntervalMin = 5
sshIPLoginWindow = 30 * time.Minute
)

type AlertTaskHelper struct {
Expand Down Expand Up @@ -512,10 +514,28 @@ func loadPanelLogin(alert dto.AlertDTO) {
}

func loadSSHLogin(alert dto.AlertDTO) {
count, isAlert, err := alertUtil.CountRecentFailedSSHLog(alert.Cycle, alert.Count)
now := time.Now()
failedWindow := time.Duration(alert.Cycle) * time.Minute
loadWindow := failedWindow
if loadWindow < sshIPLoginWindow {
loadWindow = sshIPLoginWindow
}
location, err := time.LoadLocation(common.LoadTimeZoneByCmd())
if err != nil {
global.LOG.Errorf("Failed to count recent failed ssh login logs: %v", err)
global.LOG.Errorf("Failed to load timezone for ssh login logs: %v", err)
location = time.Local
}
histories, err := loadSSHAlertHistories(defaultSSHLogDir, now.Add(-loadWindow), now, location)
if err != nil {
global.LOG.Errorf("Failed to load ssh login logs: %v", err)
}
count, records := summarizeSSHLoginHistories(
histories,
now,
failedWindow,
strings.Split(strings.TrimSpace(alert.AdvancedParams), "\n"),
)
isAlert := count >= int(alert.Count)
if isAlert {
params := []dto.Param{
{
Expand All @@ -531,12 +551,6 @@ func loadSSHLogin(alert dto.AlertDTO) {
}
sendAlerts(alert, "sshLogin", strconv.Itoa(count), "sshLogin", params)
}
whitelist := strings.Split(strings.TrimSpace(alert.AdvancedParams), "\n")
records, err := alertUtil.FindRecentSuccessLoginNotInWhitelist(30, whitelist)
if err != nil {
global.LOG.Errorf("Failed to check recent failed ip ssh login logs: %v", err)
}
records = filterSSHLoginEntriesNotInWhitelist(records, whitelist)
if len(records) > 0 {
quota := strings.Join(records, "\n")
params := []dto.Param{
Expand Down Expand Up @@ -565,20 +579,6 @@ func filterLoginLogsNotInWhitelist(records []model.LoginLog, whitelist []string)
return filtered
}

func filterSSHLoginEntriesNotInWhitelist(records []string, whitelist []string) []string {
filtered := make([]string, 0, len(records))
for _, record := range records {
ip := record
if idx := strings.Index(record, "-"); idx >= 0 {
ip = record[:idx]
}
if !isIPInWhitelist(ip, whitelist) {
filtered = append(filtered, record)
}
}
return filtered
}

func isIPInWhitelist(ip string, whitelist []string) bool {
targetIP := net.ParseIP(strings.TrimSpace(ip))
if targetIP == nil {
Expand Down Expand Up @@ -1117,3 +1117,55 @@ func calculateMinutesDifference(newDate time.Time) int {
minutesDifference := int(now.Sub(newDate).Minutes())
return minutesDifference
}

func loadSSHAlertHistories(
baseDir string,
startTime, endTime time.Time,
location *time.Location,
) ([]dto.SSHHistory, error) {
fileList, err := listSSHLogFiles(baseDir)
if err != nil {
return nil, err
}

var (
histories []dto.SSHHistory
loadErr error
)
for _, file := range fileList {
items, err := loadSSHHistoriesFromFile(file.Name, "", "", startTime, endTime, file.Year, location)
if err != nil {
loadErr = errors.Join(loadErr, fmt.Errorf("load SSH log file %s: %w", file.Name, err))
continue
}
histories = append(histories, items...)
}
return histories, loadErr
}

func summarizeSSHLoginHistories(
histories []dto.SSHHistory,
now time.Time,
failedWindow time.Duration,
whitelist []string,
) (int, []string) {
failedStartTime := now.Add(-failedWindow)
successStartTime := now.Add(-sshIPLoginWindow)
failedCount := 0
var abnormalLogins []string

for _, item := range histories {
switch item.Status {
case constant.StatusFailed:
if isSSHLogWithinTimeRange(item.Date, failedStartTime, now) {
failedCount++
}
case constant.StatusSuccess:
if !isSSHLogWithinTimeRange(item.Date, successStartTime, now) || isIPInWhitelist(item.Address, whitelist) {
continue
}
abnormalLogins = append(abnormalLogins, fmt.Sprintf("%s-%s", item.Address, item.Date.Format(constant.DateTimeLayout)))
}
}
return failedCount, abnormalLogins
}
60 changes: 42 additions & 18 deletions agent/app/service/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import (
const sshPath = "/etc/ssh/sshd_config"
const defaultSSHPort = "22"
const sshManagedMarker = "# config by 1panel"
const defaultSSHLogDir = "/var/log"

type SSHService struct{}

Expand Down Expand Up @@ -668,21 +669,19 @@ func isSSHLogFileName(name string) bool {
return false
}

func (u *SSHService) LoadLog(ctx *gin.Context, req dto.SearchSSHLog) (int64, []dto.SSHHistory, error) {
func listSSHLogFiles(baseDir string) ([]sshFileItem, error) {
var fileList []sshFileItem
var data []dto.SSHHistory
baseDir := "/var/log"
fileItems, err := os.ReadDir(baseDir)
if err != nil {
return 0, data, err
return nil, err
}
for _, item := range fileItems {
if item.IsDir() || !isSSHLogFileName(item.Name()) {
continue
}
info, err := item.Info()
if err != nil {
return 0, data, err
return nil, err
}
if !info.Mode().IsRegular() {
continue
Expand All @@ -695,7 +694,15 @@ func (u *SSHService) LoadLog(ctx *gin.Context, req dto.SearchSSHLog) (int64, []d
}
fileList = append(fileList, sshFileItem{Name: itemPath, Year: info.ModTime().Year()})
}
fileList = sortFileList(fileList)
return sortFileList(fileList), nil
}

func (u *SSHService) LoadLog(ctx *gin.Context, req dto.SearchSSHLog) (int64, []dto.SSHHistory, error) {
var data []dto.SSHHistory
fileList, err := listSSHLogFiles(defaultSSHLogDir)
if err != nil {
return 0, data, err
}

filter := ""
if len(req.Info) != 0 {
Expand Down Expand Up @@ -742,7 +749,7 @@ func (u *SSHService) LoadLog(ctx *gin.Context, req dto.SearchSSHLog) (int64, []d
}

func (u *SSHService) CleanLog() error {
return cleanSSHLogFiles("/var/log")
return cleanSSHLogFiles(defaultSSHLogDir)
}

func cleanSSHLogFiles(baseDir string) error {
Expand Down Expand Up @@ -1290,20 +1297,11 @@ func loadSSHData(
if err != nil {
return datas, 0, 0
}
lines, err := loadSSHLogLines(filePath)
histories, err := loadSSHHistoriesFromFile(filePath, status, filter, startTime, endTime, currentYear, nyc)
if err != nil {
return datas, 0, 0
}
items := collectSSHLogItems(lines, filter, status)
for i := len(items) - 1; i >= 0; i-- {
itemData := items[i].History
if !matchSSHLogStatus(status, itemData.Status) || !checkIsStandard(itemData) {
continue
}
itemData.Date = loadDate(currentYear, itemData.DateStr, nyc)
if !isSSHLogWithinTimeRange(itemData.Date, startTime, endTime) {
continue
}
for _, itemData := range histories {
if successCount+failedCount >= showCountFrom && (showCountTo == -1 || successCount+failedCount < showCountTo) {
itemData.Area, _ = geo.GetIPLocation(getLoc, itemData.Address, common.GetLang(ctx))
datas = append(datas, itemData)
Expand All @@ -1317,6 +1315,32 @@ func loadSSHData(
return datas, successCount, failedCount
}

func loadSSHHistoriesFromFile(
filePath, status, filter string,
startTime, endTime time.Time,
currentYear int,
location *time.Location,
) ([]dto.SSHHistory, error) {
lines, err := loadSSHLogLines(filePath)
if err != nil {
return nil, err
}
items := collectSSHLogItems(lines, filter, status)
histories := make([]dto.SSHHistory, 0, len(items))
for i := len(items) - 1; i >= 0; i-- {
itemData := items[i].History
if !matchSSHLogStatus(status, itemData.Status) || !checkIsStandard(itemData) {
continue
}
itemData.Date = loadDate(currentYear, itemData.DateStr, location)
if !isSSHLogWithinTimeRange(itemData.Date, startTime, endTime) {
continue
}
histories = append(histories, itemData)
}
return histories, nil
}

func isSSHLogWithinTimeRange(itemTime, startTime, endTime time.Time) bool {
if startTime.IsZero() || endTime.IsZero() {
return true
Expand Down
Loading
Loading