diff --git a/internal/web/job/check_client_ip_job.go b/internal/web/job/check_client_ip_job.go index 146db0b70..f993e32d2 100644 --- a/internal/web/job/check_client_ip_job.go +++ b/internal/web/job/check_client_ip_job.go @@ -612,7 +612,8 @@ func (j *CheckClientIpJob) filterAdvancedSinceLastBan(email string, banned []IPW return actionable } -// disconnectClientTemporarily removes and re-adds a client to force disconnect banned connections +// disconnectClientTemporarily drops a client's credential for a moment, so new +// handshakes are refused; the fail2ban ban is what ends live traffic. func (j *CheckClientIpJob) disconnectClientTemporarily(inbound *model.Inbound, clientEmail string, clients []model.Client) { var xrayAPI xray.XrayAPI apiPort := j.resolveXrayAPIPort() @@ -663,14 +664,16 @@ func (j *CheckClientIpJob) disconnectClientTemporarily(inbound *model.Inbound, c } } - // Remove user to disconnect all connections + // The core's RemoveUser clears its validator, so a session already up keeps + // running -- except vless, where it also drops a reverse handler. err = xrayAPI.RemoveUser(inbound.Tag, clientEmail) if err != nil { logger.Warningf("[LIMIT_IP] Failed to remove user %s: %v", clientEmail, err) return } - // Wait a moment for disconnection to take effect + // Nothing is pending here: AlterInbound applies the removal inline, so this + // only widens the window in which new handshakes fail. time.Sleep(100 * time.Millisecond) // Re-add user to allow new connections