package admin import ( "encoding/json" "io" "net" "net/http" "strings" "sync" "time" "github.com/bjdgyc/anylink/base" ) type LockInfo struct { Description string `json:"description"` // 锁定原因 Username string `json:"username"` // 用户名 IP string `json:"ip"` // IP 地址 State *LockState `json:"state"` // 锁定状态信息 } type LockState struct { Locked bool `json:"locked"` // 是否锁定 FailureCount int `json:"attempts"` // 失败次数 LockTime time.Time `json:"lock_time"` // 锁定截止时间 LastAttempt time.Time `json:"lastAttempt"` // 最后一次尝试的时间 } type IPWhitelists struct { IP net.IP CIDR *net.IPNet } type LockManager struct { mu sync.Mutex // LoginStatus sync.Map // 登录状态 ipLocks map[string]*LockState // 全局IP锁定状态 userLocks map[string]*LockState // 全局用户锁定状态 ipUserLocks map[string]map[string]*LockState // 单用户IP锁定状态 ipWhitelists []IPWhitelists // 全局IP白名单,包含IP地址和CIDR范围 cleanupTicker *time.Ticker } var lockmanager *LockManager var once sync.Once func GetLockManager() *LockManager { once.Do(func() { lockmanager = &LockManager{ // LoginStatus: sync.Map{}, ipLocks: make(map[string]*LockState), userLocks: make(map[string]*LockState), ipUserLocks: make(map[string]map[string]*LockState), ipWhitelists: make([]IPWhitelists, 0), } }) return lockmanager } const defaultGlobalLockStateExpirationTime = 3600 func InitLockManager() { lm := GetLockManager() if base.Cfg.AntiBruteForce { if base.Cfg.GlobalLockStateExpirationTime <= 0 { base.Cfg.GlobalLockStateExpirationTime = defaultGlobalLockStateExpirationTime } lm.StartCleanupTicker() lm.InitIPWhitelist() } } func GetLocksInfo(w http.ResponseWriter, r *http.Request) { lm := GetLockManager() locksInfo := lm.GetLocksInfo() RespSucess(w, locksInfo) } func UnlockUser(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) if err != nil { RespError(w, RespInternalErr, err) return } lockinfo := LockInfo{} if err := json.Unmarshal(body, &lockinfo); err != nil { RespError(w, RespInternalErr, err) return } if lockinfo.State == nil { RespError(w, RespInternalErr, "未找到锁定用户!") return } lm := GetLockManager() lm.mu.Lock() defer lm.mu.Unlock() // 根据用户名和IP查找锁定状态 var state *LockState switch { case lockinfo.IP == "" && lockinfo.Username != "": state = lm.userLocks[lockinfo.Username] // 全局用户锁定 case lockinfo.Username != "" && lockinfo.IP != "": if userIPMap, exists := lm.ipUserLocks[lockinfo.Username]; exists { state = userIPMap[lockinfo.IP] // 单用户 IP 锁定 } default: state = lm.ipLocks[lockinfo.IP] // 全局 IP 锁定 } if state == nil || !state.Locked { RespError(w, RespInternalErr, "锁定状态未找到或已解锁") return } lm.Unlock(state) base.Info("解锁成功:", lockinfo.Description, lockinfo.Username, lockinfo.IP) RespSucess(w, "解锁成功!") } func (lm *LockManager) GetLocksInfo() []LockInfo { var locksInfo []LockInfo lm.mu.Lock() defer lm.mu.Unlock() for ip, state := range lm.ipLocks { if base.Cfg.MaxGlobalIPBanCount > 0 && state.Locked { info := LockInfo{ Description: "全局IP锁定", Username: "", IP: ip, State: &LockState{ Locked: state.Locked, FailureCount: state.FailureCount, LockTime: state.LockTime, LastAttempt: state.LastAttempt, }, } locksInfo = append(locksInfo, info) } } for username, state := range lm.userLocks { if base.Cfg.MaxGlobalUserBanCount > 0 && state.Locked { info := LockInfo{ Description: "全局用户锁定", Username: username, IP: "", State: &LockState{ Locked: state.Locked, FailureCount: state.FailureCount, LockTime: state.LockTime, LastAttempt: state.LastAttempt, }, } locksInfo = append(locksInfo, info) } } for username, ipStates := range lm.ipUserLocks { for ip, state := range ipStates { if base.Cfg.MaxBanCount > 0 && state.Locked { info := LockInfo{ Description: "单用户IP锁定", Username: username, IP: ip, State: &LockState{ Locked: state.Locked, FailureCount: state.FailureCount, LockTime: state.LockTime, LastAttempt: state.LastAttempt, }, } locksInfo = append(locksInfo, info) } } } return locksInfo } // 初始化IP白名单 func (lm *LockManager) InitIPWhitelist() { ipWhitelist := strings.Split(base.Cfg.IPWhitelist, ",") for _, ipWhitelist := range ipWhitelist { ipWhitelist = strings.TrimSpace(ipWhitelist) if ipWhitelist == "" { continue } _, ipNet, err := net.ParseCIDR(ipWhitelist) if err == nil { lm.ipWhitelists = append(lm.ipWhitelists, IPWhitelists{CIDR: ipNet}) continue } ip := net.ParseIP(ipWhitelist) if ip != nil { lm.ipWhitelists = append(lm.ipWhitelists, IPWhitelists{IP: ip}) continue } } } // 检查 IP 是否在白名单中 func (lm *LockManager) IsWhitelisted(ip string) bool { clientIP := net.ParseIP(ip) if clientIP == nil { return false } for _, ipWhitelist := range lm.ipWhitelists { if ipWhitelist.CIDR != nil && ipWhitelist.CIDR.Contains(clientIP) { return true } if ipWhitelist.IP != nil && ipWhitelist.IP.Equal(clientIP) { return true } } return false } func (lm *LockManager) StartCleanupTicker() { lm.cleanupTicker = time.NewTicker(1 * time.Minute) go func() { for range lm.cleanupTicker.C { lm.CleanupExpiredLocks() } }() } // 定期清理过期的锁定 func (lm *LockManager) CleanupExpiredLocks() { now := time.Now() lm.mu.Lock() defer lm.mu.Unlock() for ip, state := range lm.ipLocks { if !lm.CheckLockState(state, now, base.Cfg.GlobalIPBanResetTime) || now.Sub(state.LastAttempt) > time.Duration(base.Cfg.GlobalLockStateExpirationTime)*time.Second { delete(lm.ipLocks, ip) } } for user, state := range lm.userLocks { if !lm.CheckLockState(state, now, base.Cfg.GlobalUserBanResetTime) || now.Sub(state.LastAttempt) > time.Duration(base.Cfg.GlobalLockStateExpirationTime)*time.Second { delete(lm.userLocks, user) } } for user, ipMap := range lm.ipUserLocks { for ip, state := range ipMap { if !lm.CheckLockState(state, now, base.Cfg.BanResetTime) || now.Sub(state.LastAttempt) > time.Duration(base.Cfg.GlobalLockStateExpirationTime)*time.Second { delete(ipMap, ip) if len(ipMap) == 0 { delete(lm.ipUserLocks, user) } } } } } // 检查全局 IP 锁定 func (lm *LockManager) CheckGlobalIPLock(ip string, now time.Time) bool { lm.mu.Lock() defer lm.mu.Unlock() state, exists := lm.ipLocks[ip] if !exists { return false } return lm.CheckLockState(state, now, base.Cfg.GlobalIPBanResetTime) } // 检查全局用户锁定 func (lm *LockManager) CheckGlobalUserLock(username string, now time.Time) bool { // 我也不知道为什么cisco anyconnect每次连接会先传一个空用户请求···· if username == "" { return false } lm.mu.Lock() defer lm.mu.Unlock() state, exists := lm.userLocks[username] if !exists { return false } return lm.CheckLockState(state, now, base.Cfg.GlobalUserBanResetTime) } // 检查单个用户的 IP 锁定 func (lm *LockManager) CheckUserIPLock(username, ip string, now time.Time) bool { // 我也不知道为什么cisco anyconnect每次连接会先传一个空用户请求···· if username == "" { return false } lm.mu.Lock() defer lm.mu.Unlock() userIPMap, userExists := lm.ipUserLocks[username] if !userExists { return false } state, ipExists := userIPMap[ip] if !ipExists { return false } return lm.CheckLockState(state, now, base.Cfg.BanResetTime) } // 更新全局 IP 锁定状态 func (lm *LockManager) UpdateGlobalIPLock(ip string, now time.Time, success bool) { lm.mu.Lock() defer lm.mu.Unlock() state, exists := lm.ipLocks[ip] if !exists { state = &LockState{} lm.ipLocks[ip] = state } lm.UpdateLockState(state, now, success, base.Cfg.MaxGlobalIPBanCount, base.Cfg.GlobalIPLockTime) } // 更新全局用户锁定状态 func (lm *LockManager) UpdateGlobalUserLock(username string, now time.Time, success bool) { // 我也不知道为什么cisco anyconnect每次连接会先传一个空用户请求···· if username == "" { return } lm.mu.Lock() defer lm.mu.Unlock() state, exists := lm.userLocks[username] if !exists { state = &LockState{} lm.userLocks[username] = state } lm.UpdateLockState(state, now, success, base.Cfg.MaxGlobalUserBanCount, base.Cfg.GlobalUserLockTime) } // 更新单个用户的 IP 锁定状态 func (lm *LockManager) UpdateUserIPLock(username, ip string, now time.Time, success bool) { // 我也不知道为什么cisco anyconnect每次连接会先传一个空用户请求···· if username == "" { return } lm.mu.Lock() defer lm.mu.Unlock() userIPMap, userExists := lm.ipUserLocks[username] if !userExists { userIPMap = make(map[string]*LockState) lm.ipUserLocks[username] = userIPMap } state, ipExists := userIPMap[ip] if !ipExists { state = &LockState{} userIPMap[ip] = state } lm.UpdateLockState(state, now, success, base.Cfg.MaxBanCount, base.Cfg.LockTime) } // 更新锁定状态 func (lm *LockManager) UpdateLockState(state *LockState, now time.Time, success bool, maxBanCount, lockTime int) { if success { lm.Unlock(state) // 成功登录后解锁 } else { state.FailureCount++ if state.FailureCount >= maxBanCount { state.LockTime = now.Add(time.Duration(lockTime) * time.Second) state.Locked = true // 超过阈值时锁定 } } state.LastAttempt = now } // 检查锁定状态 func (lm *LockManager) CheckLockState(state *LockState, now time.Time, resetTime int) bool { if state == nil || state.LastAttempt.IsZero() { return false } // 如果超过锁定时间,重置锁定状态 if !state.LockTime.IsZero() && now.After(state.LockTime) { lm.Unlock(state) // 锁定期过后解锁 return false } // 如果超过窗口时间,重置失败计数 if now.Sub(state.LastAttempt) > time.Duration(resetTime)*time.Second { state.FailureCount = 0 return false } return state.Locked } // 解锁 func (lm *LockManager) Unlock(state *LockState) { state.FailureCount = 0 state.LockTime = time.Time{} state.Locked = false } // 检查锁定状态 func (lm *LockManager) CheckLocked(username, ipaddr string) bool { if !base.Cfg.AntiBruteForce { return true } ip, _, err := net.SplitHostPort(ipaddr) // 提取纯 IP 地址,去掉端口号 if err != nil { base.Error("检查锁定状态失败,提取IP地址错误:", ipaddr) return true } now := time.Now() // 检查IP是否在白名单中 if lm.IsWhitelisted(ip) { return true } // 检查全局 IP 锁定 if base.Cfg.MaxGlobalIPBanCount > 0 && lm.CheckGlobalIPLock(ip, now) { base.Warn("IP", ip, "is globally locked. Try again later.") return false } // 检查全局用户锁定 if base.Cfg.MaxGlobalUserBanCount > 0 && lm.CheckGlobalUserLock(username, now) { base.Warn("User", username, "is globally locked. Try again later.") return false } // 检查单个用户的 IP 锁定 if base.Cfg.MaxBanCount > 0 && lm.CheckUserIPLock(username, ip, now) { base.Warn("IP", ip, "is locked for user", username, "Try again later.") return false } return true } // 更新用户登录状态 func (lm *LockManager) UpdateLoginStatus(username, ipaddr string, loginStatus bool) { ip, _, err := net.SplitHostPort(ipaddr) // 提取纯 IP 地址,去掉端口号 if err != nil { base.Error("更新登录状态失败,提取IP地址错误:", ipaddr) return } now := time.Now() // 更新用户登录状态 lm.UpdateGlobalIPLock(ip, now, loginStatus) lm.UpdateGlobalUserLock(username, now, loginStatus) lm.UpdateUserIPLock(username, ip, now, loginStatus) }