Добавлена система блокировки IP-адресов для защиты от перегрузки памяти
This commit is contained in:
parent
d0225d92c0
commit
195d50d820
4 changed files with 221 additions and 0 deletions
|
|
@ -6,6 +6,8 @@ using System.Runtime.InteropServices;
|
|||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.Connection.IPIntel;
|
||||
using Content.Server.Connection.IPBlocking;
|
||||
using Content.Shared.Connection.IPBlocking;
|
||||
using Content.Server.Database;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.Preferences.Managers;
|
||||
|
|
@ -59,6 +61,7 @@ namespace Content.Server.Connection
|
|||
private readonly Dictionary<NetUserId, TimeSpan> _temporaryBypasses = [];
|
||||
private readonly Dictionary<NetUserId, DateTime> _temporaryConnectionAllowed = [];
|
||||
private IPIntel.IPIntel _ipintel = default!;
|
||||
private IPBlockingSystem _ipBlockingSystem = default!;
|
||||
|
||||
public event EventHandler<PlayerConnectingWithBanEvent>? PlayerConnectingWithBan;
|
||||
|
||||
|
|
@ -73,6 +76,14 @@ namespace Content.Server.Connection
|
|||
|
||||
_ipintel = new IPIntel.IPIntel(new IPIntelApi(_http, _cfg), _db, _cfg, _logManager, _chatManager, _gameTiming);
|
||||
|
||||
// Инициализация системы блокировки IP
|
||||
_ipBlockingSystem = new IPBlockingSystem();
|
||||
IoCManager.Instance!.InjectDependencies(_ipBlockingSystem);
|
||||
_ipBlockingSystem.Initialize();
|
||||
|
||||
// Регистрация в IoC для использования в других системах
|
||||
IoCManager.Instance.RegisterInstance<IIPBlockingSystem>(_ipBlockingSystem, true);
|
||||
|
||||
IoCManager.Instance!.TryResolveType(out _sponsorsMgr);
|
||||
_netMgr.Connecting += NetMgrOnConnecting;
|
||||
_netMgr.AssignUserIdCallback = AssignUserIdCallback;
|
||||
|
|
@ -124,6 +135,16 @@ namespace Content.Server.Connection
|
|||
{
|
||||
_sawmill.Error("IPIntel update failed:" + e);
|
||||
}
|
||||
|
||||
// Периодическая очистка истекших блокировок IP
|
||||
try
|
||||
{
|
||||
_ipBlockingSystem.Update();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_sawmill.Error("IPBlockingSystem update failed:" + e);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task NetMgrOnConnecting(NetConnectingArgs e)
|
||||
|
|
|
|||
156
Content.Server/Connection/IPBlocking/IPBlockingSystem.cs
Normal file
156
Content.Server/Connection/IPBlocking/IPBlockingSystem.cs
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
using Content.Shared.Connection.IPBlocking;
|
||||
|
||||
namespace Content.Server.Connection.IPBlocking;
|
||||
|
||||
/// <summary>
|
||||
/// Система блокировки IP-адресов для защиты от перегрузки памяти
|
||||
/// при получении подозрительных запросов с некорректными длинами ответов.
|
||||
/// </summary>
|
||||
public sealed class IPBlockingSystem : IIPBlockingSystem
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
private readonly ConcurrentDictionary<IPAddress, DateTime> _blockedIPs = new();
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
private bool _enabled;
|
||||
private int _blockDurationSeconds;
|
||||
private int _maxResponseLength;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_sawmill = _logManager.GetSawmill("ipblocking");
|
||||
|
||||
_cfg.OnValueChanged(CCVars.GameIPBlockingEnabled, b => _enabled = b, true);
|
||||
_cfg.OnValueChanged(CCVars.GameIPBlockingDuration, b => _blockDurationSeconds = b, true);
|
||||
_cfg.OnValueChanged(CCVars.GameIPBlockingMaxResponseLength, b => _maxResponseLength = b, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Проверяет, заблокирован ли указанный IP-адрес.
|
||||
/// </summary>
|
||||
public bool IsBlocked(IPAddress ip)
|
||||
{
|
||||
if (!_enabled)
|
||||
return false;
|
||||
|
||||
if (!_blockedIPs.TryGetValue(ip, out var unblockTime))
|
||||
return false;
|
||||
|
||||
// Проверяем, не истекла ли блокировка
|
||||
if (DateTime.UtcNow >= unblockTime)
|
||||
{
|
||||
_blockedIPs.TryRemove(ip, out _);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Блокирует IP-адрес на указанное время с указанной причиной.
|
||||
/// </summary>
|
||||
public void BlockIP(IPAddress ip, TimeSpan duration, string reason)
|
||||
{
|
||||
if (!_enabled)
|
||||
return;
|
||||
|
||||
var unblockTime = DateTime.UtcNow + duration;
|
||||
_blockedIPs.AddOrUpdate(ip, unblockTime, (_, _) => unblockTime);
|
||||
|
||||
_sawmill.Warning($"Заблокирован IP {ip} на {duration.TotalMinutes:F1} минут. Причина: {reason}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Блокирует IP-адрес на время, указанное в CVar, с указанной причиной.
|
||||
/// </summary>
|
||||
public void BlockIP(IPAddress ip, string reason)
|
||||
{
|
||||
var duration = TimeSpan.FromSeconds(_blockDurationSeconds);
|
||||
BlockIP(ip, duration, reason);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Проверяет длину ответа и блокирует IP при обнаружении подозрительного значения.
|
||||
/// </summary>
|
||||
/// <returns>true, если длина подозрительная и IP был заблокирован</returns>
|
||||
public bool CheckAndBlockSuspiciousLength(IPAddress ip, int length, string context)
|
||||
{
|
||||
if (!_enabled)
|
||||
return false;
|
||||
|
||||
// Проверяем на отрицательные или слишком большие значения
|
||||
if (length < 0 || length > _maxResponseLength)
|
||||
{
|
||||
var reason = $"Подозрительная длина ответа: {length} байт (контекст: {context})";
|
||||
BlockIP(ip, reason);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает максимально допустимую длину ответа.
|
||||
/// </summary>
|
||||
public int GetMaxResponseLength()
|
||||
{
|
||||
return _maxResponseLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Очищает истекшие блокировки. Должен вызываться периодически.
|
||||
/// </summary>
|
||||
public void Update()
|
||||
{
|
||||
if (!_enabled)
|
||||
return;
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
var keysToRemove = new List<IPAddress>();
|
||||
|
||||
foreach (var (ip, unblockTime) in _blockedIPs)
|
||||
{
|
||||
if (now >= unblockTime)
|
||||
{
|
||||
keysToRemove.Add(ip);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var ip in keysToRemove)
|
||||
{
|
||||
_blockedIPs.TryRemove(ip, out _);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Разблокирует IP-адрес вручную.
|
||||
/// </summary>
|
||||
public void UnblockIP(IPAddress ip)
|
||||
{
|
||||
if (_blockedIPs.TryRemove(ip, out _))
|
||||
{
|
||||
_sawmill.Info($"IP {ip} разблокирован вручную");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает количество заблокированных IP-адресов.
|
||||
/// </summary>
|
||||
public int GetBlockedCount()
|
||||
{
|
||||
return _blockedIPs.Count;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -310,6 +310,24 @@ public sealed partial class CCVars
|
|||
public static readonly CVarDef<float> GameIPIntelAlertAdminWarnRating =
|
||||
CVarDef.Create("game.ipintel_alert_admin_warn_rating", 0f, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// Включить систему блокировки IP-адресов для защиты от перегрузки памяти.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<bool> GameIPBlockingEnabled =
|
||||
CVarDef.Create("game.ipblocking_enabled", true, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// Время блокировки IP-адреса в секундах при обнаружении подозрительного запроса.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<int> GameIPBlockingDuration =
|
||||
CVarDef.Create("game.ipblocking_duration", 900, CVar.SERVERONLY); // 15 минут по умолчанию
|
||||
|
||||
/// <summary>
|
||||
/// Максимальная допустимая длина ответа в байтах. Запросы с большей длиной будут блокироваться.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<int> GameIPBlockingMaxResponseLength =
|
||||
CVarDef.Create("game.ipblocking_max_response_length", 10485760, CVar.SERVERONLY); // 10MB по умолчанию
|
||||
|
||||
/// <summary>
|
||||
/// Make people bonk when trying to climb certain objects like tables.
|
||||
/// </summary>
|
||||
|
|
|
|||
26
Content.Shared/Connection/IPBlocking/IIPBlockingSystem.cs
Normal file
26
Content.Shared/Connection/IPBlocking/IIPBlockingSystem.cs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
using System.Net;
|
||||
|
||||
namespace Content.Shared.Connection.IPBlocking;
|
||||
|
||||
/// <summary>
|
||||
/// Интерфейс для системы блокировки IP-адресов.
|
||||
/// </summary>
|
||||
public interface IIPBlockingSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Проверяет, заблокирован ли указанный IP-адрес.
|
||||
/// </summary>
|
||||
bool IsBlocked(IPAddress ip);
|
||||
|
||||
/// <summary>
|
||||
/// Проверяет длину ответа и блокирует IP при обнаружении подозрительного значения.
|
||||
/// </summary>
|
||||
/// <returns>true, если длина подозрительная и IP был заблокирован</returns>
|
||||
bool CheckAndBlockSuspiciousLength(IPAddress ip, int length, string context);
|
||||
|
||||
/// <summary>
|
||||
/// Получает максимально допустимую длину ответа.
|
||||
/// </summary>
|
||||
int GetMaxResponseLength();
|
||||
}
|
||||
|
||||
Loading…
Add table
Reference in a new issue