mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-04-14 19:45:47 +00:00
Three layered fixes targeting the distinct causes of overage: Bucket C (catastrophic): flush pending Xray stats to DB before every scheduled Xray restart so in-memory counters are never silently zeroed. - web/service/xray.go: add FlushTrafficToDB() - web/web.go: call FlushTrafficToDB() in the 30 s restart cron before RestartXray(false) Bucket A (in-flight gap): drain per-user Xray stats counters immediately after RemoveUser() succeeds, capturing bytes accumulated since the last bulk GetTraffic(reset=true) cycle. - xray/api.go: add DrainUserTraffic(email) using GetStats gRPC with reset - web/service/inbound.go: call DrainUserTraffic and persist delta in disableInvalidClients() Bucket B (active TCP connections survive removal): insert iptables DROP rules for each known client IP on the inbound port so established connections are killed immediately, not just new ones. - util/iptables/iptables.go: new package managing the 3X-UI-BLOCK chain (EnsureChain, FlushChain, BlockIP, UnblockIP, ListRules); gracefully degrades when iptables is unavailable - web/job/unblock_ips_job.go: @every 5m cleanup job removes rules older than maxBlockAgeSecs - web/service/inbound.go: blockClientIPs() called after successful RemoveUser(); unblockClientIPs() called after successful AddUser() in autoRenewClients() so renewed clients can reconnect - web/web.go: EnsureChain + FlushChain on startup; register unblock job
40 lines
1.2 KiB
Go
40 lines
1.2 KiB
Go
package job
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/mhsanaei/3x-ui/v2/logger"
|
|
"github.com/mhsanaei/3x-ui/v2/util/iptables"
|
|
)
|
|
|
|
const maxBlockAgeSecs int64 = 600 // 10 minutes
|
|
|
|
// UnblockIPsJob removes expired iptables DROP rules from the 3X-UI-BLOCK chain.
|
|
// Rules older than maxBlockAgeSecs are removed to prevent the firewall table
|
|
// from growing unbounded and to unblock IPs that may have been re-assigned.
|
|
type UnblockIPsJob struct{}
|
|
|
|
// NewUnblockIPsJob creates a new instance of the IP unblock cleanup job.
|
|
func NewUnblockIPsJob() *UnblockIPsJob {
|
|
return &UnblockIPsJob{}
|
|
}
|
|
|
|
// Run enumerates all rules in the 3X-UI-BLOCK chain and removes any that are
|
|
// older than maxBlockAgeSecs.
|
|
func (j *UnblockIPsJob) Run() {
|
|
rules, err := iptables.ListRules()
|
|
if err != nil {
|
|
logger.Debug("UnblockIPsJob: failed to list iptables rules:", err)
|
|
return
|
|
}
|
|
now := time.Now().Unix()
|
|
for _, rule := range rules {
|
|
if rule.InsertedAt > 0 && (now-rule.InsertedAt) > maxBlockAgeSecs {
|
|
if err := iptables.UnblockIP(rule.IP, rule.Port); err != nil {
|
|
logger.Warning("UnblockIPsJob: failed to unblock", rule.IP, rule.Port, err)
|
|
} else {
|
|
logger.Debug("UnblockIPsJob: unblocked expired rule", rule.IP, rule.Port)
|
|
}
|
|
}
|
|
}
|
|
}
|