From f76cc6036ebf446553ee5201321879538dafe3b2 Mon Sep 17 00:00:00 2001 From: teravus Date: Mon, 7 Oct 2013 21:35:55 -0500 Subject: [PATCH] * Added a Basic DOS protection container/base object for the most common HTTP Server handlers. XMLRPC Handler, GenericHttpHandler and StreamHandler * Applied the XmlRpcBasicDOSProtector.cs to the login service as both an example, and good practice. * Applied the BaseStreamHandlerBasicDOSProtector.cs to the friends service as an example of the DOS Protector on StreamHandlers * Added CircularBuffer, used for CPU and Memory friendly rate monitoring. * DosProtector has 2 states, 1. Just Check for blocked users and check general velocity, 2. Track velocity per user, It only jumps to 2 if it's getting a lot of requests, and state 1 is about as resource friendly as if it wasn't even there. --- OpenSim/Framework/CircularBuffer.cs | 312 ++++++++++++++++++ .../BaseStreamHandlerBasicDOSProtector.cs | 233 +++++++++++++ .../GenericHTTPBasicDOSProtector.cs | 238 +++++++++++++ .../HttpServer/XmlRpcBasicDOSProtector.cs | 211 ++++++++++++ .../Avatar/Friends/FriendsRequestHandler.cs | 12 +- .../World/WorldMap/WorldMapModule.cs | 21 +- .../Handlers/Asset/AssetServerGetHandler.cs | 12 +- .../Server/Handlers/Login/LLLoginHandlers.cs | 11 + .../Login/LLLoginServiceInConnector.cs | 15 +- ThirdPartyLicenses/CircularBuffer.txt | 16 + bin/Robust.ini.example | 19 ++ .../StandaloneCommon.ini.example | 19 +- 12 files changed, 1112 insertions(+), 7 deletions(-) create mode 100644 OpenSim/Framework/CircularBuffer.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs create mode 100644 ThirdPartyLicenses/CircularBuffer.txt diff --git a/OpenSim/Framework/CircularBuffer.cs b/OpenSim/Framework/CircularBuffer.cs new file mode 100644 index 0000000000..e919337a70 --- /dev/null +++ b/OpenSim/Framework/CircularBuffer.cs @@ -0,0 +1,312 @@ +/* +Copyright (c) 2012, Alex Regueiro +All rights reserved. +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; + +namespace OpenSim.Framework +{ + public class CircularBuffer : ICollection, IEnumerable, ICollection, IEnumerable + { + private int capacity; + private int size; + private int head; + private int tail; + private T[] buffer; + + [NonSerialized()] + private object syncRoot; + + public CircularBuffer(int capacity) + : this(capacity, false) + { + } + + public CircularBuffer(int capacity, bool allowOverflow) + { + if (capacity < 0) + throw new ArgumentException("Needs to have at least 1","capacity"); + + this.capacity = capacity; + size = 0; + head = 0; + tail = 0; + buffer = new T[capacity]; + AllowOverflow = allowOverflow; + } + + public bool AllowOverflow + { + get; + set; + } + + public int Capacity + { + get { return capacity; } + set + { + if (value == capacity) + return; + + if (value < size) + throw new ArgumentOutOfRangeException("value","Capacity is too small."); + + var dst = new T[value]; + if (size > 0) + CopyTo(dst); + buffer = dst; + + capacity = value; + } + } + + public int Size + { + get { return size; } + } + + public bool Contains(T item) + { + int bufferIndex = head; + var comparer = EqualityComparer.Default; + for (int i = 0; i < size; i++, bufferIndex++) + { + if (bufferIndex == capacity) + bufferIndex = 0; + + if (item == null && buffer[bufferIndex] == null) + return true; + else if ((buffer[bufferIndex] != null) && + comparer.Equals(buffer[bufferIndex], item)) + return true; + } + + return false; + } + + public void Clear() + { + size = 0; + head = 0; + tail = 0; + } + + public int Put(T[] src) + { + return Put(src, 0, src.Length); + } + + public int Put(T[] src, int offset, int count) + { + if (!AllowOverflow && count > capacity - size) + throw new InvalidOperationException("Buffer Overflow"); + + int srcIndex = offset; + for (int i = 0; i < count; i++, tail++, srcIndex++) + { + if (tail == capacity) + tail = 0; + buffer[tail] = src[srcIndex]; + } + size = Math.Min(size + count, capacity); + return count; + } + + public void Put(T item) + { + if (!AllowOverflow && size == capacity) + throw new InvalidOperationException("Buffer Overflow"); + + buffer[tail] = item; + if (++tail == capacity) + tail = 0; + size++; + } + + public void Skip(int count) + { + head += count; + if (head >= capacity) + head -= capacity; + } + + public T[] Get(int count) + { + var dst = new T[count]; + Get(dst); + return dst; + } + + public int Get(T[] dst) + { + return Get(dst, 0, dst.Length); + } + + public int Get(T[] dst, int offset, int count) + { + int realCount = Math.Min(count, size); + int dstIndex = offset; + for (int i = 0; i < realCount; i++, head++, dstIndex++) + { + if (head == capacity) + head = 0; + dst[dstIndex] = buffer[head]; + } + size -= realCount; + return realCount; + } + + public T Get() + { + if (size == 0) + throw new InvalidOperationException("Buffer Empty"); + + var item = buffer[head]; + if (++head == capacity) + head = 0; + size--; + return item; + } + + public void CopyTo(T[] array) + { + CopyTo(array, 0); + } + + public void CopyTo(T[] array, int arrayIndex) + { + CopyTo(0, array, arrayIndex, size); + } + + public void CopyTo(int index, T[] array, int arrayIndex, int count) + { + if (count > size) + throw new ArgumentOutOfRangeException("count", "Count Too Large"); + + int bufferIndex = head; + for (int i = 0; i < count; i++, bufferIndex++, arrayIndex++) + { + if (bufferIndex == capacity) + bufferIndex = 0; + array[arrayIndex] = buffer[bufferIndex]; + } + } + + public IEnumerator GetEnumerator() + { + int bufferIndex = head; + for (int i = 0; i < size; i++, bufferIndex++) + { + if (bufferIndex == capacity) + bufferIndex = 0; + + yield return buffer[bufferIndex]; + } + } + + public T[] GetBuffer() + { + return buffer; + } + + public T[] ToArray() + { + var dst = new T[size]; + CopyTo(dst); + return dst; + } + + #region ICollection Members + + int ICollection.Count + { + get { return Size; } + } + + bool ICollection.IsReadOnly + { + get { return false; } + } + + void ICollection.Add(T item) + { + Put(item); + } + + bool ICollection.Remove(T item) + { + if (size == 0) + return false; + + Get(); + return true; + } + + #endregion + + #region IEnumerable Members + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + + #region ICollection Members + + int ICollection.Count + { + get { return Size; } + } + + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get + { + if (syncRoot == null) + Interlocked.CompareExchange(ref syncRoot, new object(), null); + return syncRoot; + } + } + + void ICollection.CopyTo(Array array, int arrayIndex) + { + CopyTo((T[])array, arrayIndex); + } + + #endregion + + #region IEnumerable Members + + IEnumerator IEnumerable.GetEnumerator() + { + return (IEnumerator)GetEnumerator(); + } + + #endregion + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs new file mode 100644 index 0000000000..8fc9a8aa92 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs @@ -0,0 +1,233 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using OpenSim.Framework; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using log4net; + +namespace OpenSim.Framework.Servers.HttpServer +{ + /// + /// BaseStreamHandlerBasicDOSProtector Base streamed request handler. + /// + /// + /// Inheriting classes should override ProcessRequest() rather than Handle() + /// + public abstract class BaseStreamHandlerBasicDOSProtector : BaseRequestHandler, IStreamedRequestHandler + { + private readonly CircularBuffer _generalRequestTimes; + private readonly BasicDosProtectorOptions _options; + private readonly Dictionary> _deeperInspection; + private readonly Dictionary _tempBlocked; + private readonly System.Timers.Timer _forgetTimer; + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private readonly System.Threading.ReaderWriterLockSlim _lockSlim = new System.Threading.ReaderWriterLockSlim(); + + protected BaseStreamHandlerBasicDOSProtector(string httpMethod, string path, BasicDosProtectorOptions options) : this(httpMethod, path, null, null, options) {} + + protected BaseStreamHandlerBasicDOSProtector(string httpMethod, string path, string name, string description, BasicDosProtectorOptions options) + : base(httpMethod, path, name, description) + { + _generalRequestTimes = new CircularBuffer(options.MaxRequestsInTimeframe + 1, true); + _generalRequestTimes.Put(0); + _options = options; + _deeperInspection = new Dictionary>(); + _tempBlocked = new Dictionary(); + _forgetTimer = new System.Timers.Timer(); + _forgetTimer.Elapsed += delegate + { + _forgetTimer.Enabled = false; + + List removes = new List(); + _lockSlim.EnterReadLock(); + foreach (string str in _tempBlocked.Keys) + { + if ( + Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), + _tempBlocked[str]) > 0) + removes.Add(str); + } + _lockSlim.ExitReadLock(); + lock (_deeperInspection) + { + _lockSlim.EnterWriteLock(); + for (int i = 0; i < removes.Count; i++) + { + _tempBlocked.Remove(removes[i]); + _deeperInspection.Remove(removes[i]); + } + _lockSlim.ExitWriteLock(); + } + foreach (string str in removes) + { + m_log.InfoFormat("[{0}] client: {1} is no longer blocked.", + _options.ReportingName, str); + } + _lockSlim.EnterReadLock(); + if (_tempBlocked.Count > 0) + _forgetTimer.Enabled = true; + _lockSlim.ExitReadLock(); + }; + + _forgetTimer.Interval = _options.ForgetTimeSpan.TotalMilliseconds; + } + + public virtual byte[] Handle( + string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + byte[] result; + RequestsReceived++; + //httpRequest.Headers + + if (_options.MaxRequestsInTimeframe < 1 || _options.RequestTimeSpan.TotalMilliseconds < 1) + { + result = ProcessRequest(path, request, httpRequest, httpResponse); + RequestsHandled++; + return result; + + } + + string clientstring = GetClientString(httpRequest); + + _lockSlim.EnterReadLock(); + if (_tempBlocked.ContainsKey(clientstring)) + { + _lockSlim.ExitReadLock(); + + if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod) + { + result = ThrottledRequest(path, request, httpRequest, httpResponse); + RequestsHandled++; + return result; + } + else + throw new System.Security.SecurityException("Throttled"); + } + _lockSlim.ExitReadLock(); + + _generalRequestTimes.Put(Util.EnvironmentTickCount()); + + if (_generalRequestTimes.Size == _generalRequestTimes.Capacity && + (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _generalRequestTimes.Get()) < + _options.RequestTimeSpan.TotalMilliseconds)) + { + //Trigger deeper inspection + if (DeeperInspection(httpRequest)) + { + result = ProcessRequest(path, request, httpRequest, httpResponse); + RequestsHandled++; + return result; + } + if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod) + { + result = ThrottledRequest(path, request, httpRequest, httpResponse); + RequestsHandled++; + return result; + } + else + throw new System.Security.SecurityException("Throttled"); + } + + result =ProcessRequest(path, request, httpRequest, httpResponse); + RequestsHandled++; + + return result; + } + + protected virtual byte[] ProcessRequest( + string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + return null; + } + + protected virtual byte[] ThrottledRequest( + string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + return new byte[0]; + } + + private bool DeeperInspection(IOSHttpRequest httpRequest) + { + lock (_deeperInspection) + { + string clientstring = GetClientString(httpRequest); + + + if (_deeperInspection.ContainsKey(clientstring)) + { + _deeperInspection[clientstring].Put(Util.EnvironmentTickCount()); + if (_deeperInspection[clientstring].Size == _deeperInspection[clientstring].Capacity && + (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _deeperInspection[clientstring].Get()) < + _options.RequestTimeSpan.TotalMilliseconds)) + { + _lockSlim.EnterWriteLock(); + if (!_tempBlocked.ContainsKey(clientstring)) + _tempBlocked.Add(clientstring, Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds); + else + _tempBlocked[clientstring] = Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds; + _lockSlim.ExitWriteLock(); + + m_log.WarnFormat("[{0}]: client: {1} is blocked for {2} milliseconds, X-ForwardedForAllowed status is {3}, endpoint:{4}", _options.ReportingName, clientstring, _options.ForgetTimeSpan.TotalMilliseconds, _options.AllowXForwardedFor, GetRemoteAddr(httpRequest)); + return false; + } + //else + // return true; + } + else + { + _deeperInspection.Add(clientstring, new CircularBuffer(_options.MaxRequestsInTimeframe + 1, true)); + _deeperInspection[clientstring].Put(Util.EnvironmentTickCount()); + _forgetTimer.Enabled = true; + } + + } + return true; + } + private string GetRemoteAddr(IOSHttpRequest httpRequest) + { + string remoteaddr = string.Empty; + if (httpRequest.Headers["remote_addr"] != null) + remoteaddr = httpRequest.Headers["remote_addr"]; + + return remoteaddr; + } + + private string GetClientString(IOSHttpRequest httpRequest) + { + string clientstring = string.Empty; + + if (_options.AllowXForwardedFor && httpRequest.Headers["x-forwarded-for"] != null) + clientstring = httpRequest.Headers["x-forwarded-for"]; + else + clientstring = GetRemoteAddr(httpRequest); + + return clientstring; + + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs new file mode 100644 index 0000000000..5fc999a01c --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs @@ -0,0 +1,238 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Net; +using OpenSim.Framework; +using log4net; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public class GenericHTTPDOSProtector + { + private readonly GenericHTTPMethod _normalMethod; + private readonly GenericHTTPMethod _throttledMethod; + private readonly CircularBuffer _generalRequestTimes; + private readonly BasicDosProtectorOptions _options; + private readonly Dictionary> _deeperInspection; + private readonly Dictionary _tempBlocked; + private readonly System.Timers.Timer _forgetTimer; + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private readonly System.Threading.ReaderWriterLockSlim _lockSlim = new System.Threading.ReaderWriterLockSlim(); + + public GenericHTTPDOSProtector(GenericHTTPMethod normalMethod, GenericHTTPMethod throttledMethod, BasicDosProtectorOptions options) + { + _normalMethod = normalMethod; + _throttledMethod = throttledMethod; + _generalRequestTimes = new CircularBuffer(options.MaxRequestsInTimeframe + 1, true); + _generalRequestTimes.Put(0); + _options = options; + _deeperInspection = new Dictionary>(); + _tempBlocked = new Dictionary(); + _forgetTimer = new System.Timers.Timer(); + _forgetTimer.Elapsed += delegate + { + _forgetTimer.Enabled = false; + + List removes = new List(); + _lockSlim.EnterReadLock(); + foreach (string str in _tempBlocked.Keys) + { + if ( + Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), + _tempBlocked[str]) > 0) + removes.Add(str); + } + _lockSlim.ExitReadLock(); + lock (_deeperInspection) + { + _lockSlim.EnterWriteLock(); + for (int i = 0; i < removes.Count; i++) + { + _tempBlocked.Remove(removes[i]); + _deeperInspection.Remove(removes[i]); + } + _lockSlim.ExitWriteLock(); + } + foreach (string str in removes) + { + m_log.InfoFormat("[{0}] client: {1} is no longer blocked.", + _options.ReportingName, str); + } + _lockSlim.EnterReadLock(); + if (_tempBlocked.Count > 0) + _forgetTimer.Enabled = true; + _lockSlim.ExitReadLock(); + }; + + _forgetTimer.Interval = _options.ForgetTimeSpan.TotalMilliseconds; + } + public Hashtable Process(Hashtable request) + { + if (_options.MaxRequestsInTimeframe < 1) + return _normalMethod(request); + if (_options.RequestTimeSpan.TotalMilliseconds < 1) + return _normalMethod(request); + + string clientstring = GetClientString(request); + + _lockSlim.EnterReadLock(); + if (_tempBlocked.ContainsKey(clientstring)) + { + _lockSlim.ExitReadLock(); + + if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod) + return _throttledMethod(request); + else + throw new System.Security.SecurityException("Throttled"); + } + _lockSlim.ExitReadLock(); + + _generalRequestTimes.Put(Util.EnvironmentTickCount()); + + if (_generalRequestTimes.Size == _generalRequestTimes.Capacity && + (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _generalRequestTimes.Get()) < + _options.RequestTimeSpan.TotalMilliseconds)) + { + //Trigger deeper inspection + if (DeeperInspection(request)) + return _normalMethod(request); + if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod) + return _throttledMethod(request); + else + throw new System.Security.SecurityException("Throttled"); + } + Hashtable resp = null; + try + { + resp = _normalMethod(request); + } + catch (Exception) + { + + throw; + } + + return resp; + } + private bool DeeperInspection(Hashtable request) + { + lock (_deeperInspection) + { + string clientstring = GetClientString(request); + + + if (_deeperInspection.ContainsKey(clientstring)) + { + _deeperInspection[clientstring].Put(Util.EnvironmentTickCount()); + if (_deeperInspection[clientstring].Size == _deeperInspection[clientstring].Capacity && + (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _deeperInspection[clientstring].Get()) < + _options.RequestTimeSpan.TotalMilliseconds)) + { + _lockSlim.EnterWriteLock(); + if (!_tempBlocked.ContainsKey(clientstring)) + _tempBlocked.Add(clientstring, Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds); + else + _tempBlocked[clientstring] = Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds; + _lockSlim.ExitWriteLock(); + + m_log.WarnFormat("[{0}]: client: {1} is blocked for {2} milliseconds, X-ForwardedForAllowed status is {3}, endpoint:{4}", _options.ReportingName, clientstring, _options.ForgetTimeSpan.TotalMilliseconds, _options.AllowXForwardedFor, GetRemoteAddr(request)); + return false; + } + //else + // return true; + } + else + { + _deeperInspection.Add(clientstring, new CircularBuffer(_options.MaxRequestsInTimeframe + 1, true)); + _deeperInspection[clientstring].Put(Util.EnvironmentTickCount()); + _forgetTimer.Enabled = true; + } + + } + return true; + } + + private string GetRemoteAddr(Hashtable request) + { + string remoteaddr = ""; + if (!request.ContainsKey("headers")) + return remoteaddr; + Hashtable requestinfo = (Hashtable)request["headers"]; + if (!requestinfo.ContainsKey("remote_addr")) + return remoteaddr; + object remote_addrobj = requestinfo["remote_addr"]; + if (remote_addrobj != null) + { + if (!string.IsNullOrEmpty(remote_addrobj.ToString())) + { + remoteaddr = remote_addrobj.ToString(); + } + + } + return remoteaddr; + } + + private string GetClientString(Hashtable request) + { + string clientstring = ""; + if (!request.ContainsKey("headers")) + return clientstring; + + Hashtable requestinfo = (Hashtable)request["headers"]; + if (_options.AllowXForwardedFor && requestinfo.ContainsKey("x-forwarded-for")) + { + object str = requestinfo["x-forwarded-for"]; + if (str != null) + { + if (!string.IsNullOrEmpty(str.ToString())) + { + return str.ToString(); + } + } + } + if (!requestinfo.ContainsKey("remote_addr")) + return clientstring; + + object remote_addrobj = requestinfo["remote_addr"]; + if (remote_addrobj != null) + { + if (!string.IsNullOrEmpty(remote_addrobj.ToString())) + { + clientstring = remote_addrobj.ToString(); + } + } + + return clientstring; + + } + + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs new file mode 100644 index 0000000000..ae59c95e53 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs @@ -0,0 +1,211 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Net; +using Nwc.XmlRpc; +using OpenSim.Framework; +using log4net; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public enum ThrottleAction + { + DoThrottledMethod, + DoThrow + } + + public class XmlRpcBasicDOSProtector + { + private readonly XmlRpcMethod _normalMethod; + private readonly XmlRpcMethod _throttledMethod; + private readonly CircularBuffer _generalRequestTimes; // General request checker + private readonly BasicDosProtectorOptions _options; + private readonly Dictionary> _deeperInspection; // per client request checker + private readonly Dictionary _tempBlocked; // blocked list + private readonly System.Timers.Timer _forgetTimer; // Cleanup timer + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private readonly System.Threading.ReaderWriterLockSlim _lockSlim = new System.Threading.ReaderWriterLockSlim(); + + public XmlRpcBasicDOSProtector(XmlRpcMethod normalMethod, XmlRpcMethod throttledMethod,BasicDosProtectorOptions options) + { + _normalMethod = normalMethod; + _throttledMethod = throttledMethod; + _generalRequestTimes = new CircularBuffer(options.MaxRequestsInTimeframe + 1,true); + _generalRequestTimes.Put(0); + _options = options; + _deeperInspection = new Dictionary>(); + _tempBlocked = new Dictionary(); + _forgetTimer = new System.Timers.Timer(); + _forgetTimer.Elapsed += delegate + { + _forgetTimer.Enabled = false; + + List removes = new List(); + _lockSlim.EnterReadLock(); + foreach (string str in _tempBlocked.Keys) + { + if ( + Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), + _tempBlocked[str]) > 0) + removes.Add(str); + } + _lockSlim.ExitReadLock(); + lock (_deeperInspection) + { + _lockSlim.EnterWriteLock(); + for (int i = 0; i < removes.Count; i++) + { + _tempBlocked.Remove(removes[i]); + _deeperInspection.Remove(removes[i]); + } + _lockSlim.ExitWriteLock(); + } + foreach (string str in removes) + { + m_log.InfoFormat("[{0}] client: {1} is no longer blocked.", + _options.ReportingName, str); + } + _lockSlim.EnterReadLock(); + if (_tempBlocked.Count > 0) + _forgetTimer.Enabled = true; + _lockSlim.ExitReadLock(); + }; + + _forgetTimer.Interval = _options.ForgetTimeSpan.TotalMilliseconds; + } + public XmlRpcResponse Process(XmlRpcRequest request, IPEndPoint client) + { + // If these are set like this, this is disabled + if (_options.MaxRequestsInTimeframe < 1 || _options.RequestTimeSpan.TotalMilliseconds < 1) + return _normalMethod(request, client); + + string clientstring = GetClientString(request, client); + + _lockSlim.EnterReadLock(); + if (_tempBlocked.ContainsKey(clientstring)) + { + _lockSlim.ExitReadLock(); + + if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod) + return _throttledMethod(request, client); + else + throw new System.Security.SecurityException("Throttled"); + } + _lockSlim.ExitReadLock(); + + _generalRequestTimes.Put(Util.EnvironmentTickCount()); + + if (_generalRequestTimes.Size == _generalRequestTimes.Capacity && + (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _generalRequestTimes.Get()) < + _options.RequestTimeSpan.TotalMilliseconds)) + { + //Trigger deeper inspection + if (DeeperInspection(request, client)) + return _normalMethod(request, client); + if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod) + return _throttledMethod(request, client); + else + throw new System.Security.SecurityException("Throttled"); + } + XmlRpcResponse resp = null; + + resp = _normalMethod(request, client); + + return resp; + } + + // If the service is getting more hits per expected timeframe then it starts to separate them out by client + private bool DeeperInspection(XmlRpcRequest request, IPEndPoint client) + { + lock (_deeperInspection) + { + string clientstring = GetClientString(request, client); + + + if (_deeperInspection.ContainsKey(clientstring)) + { + _deeperInspection[clientstring].Put(Util.EnvironmentTickCount()); + if (_deeperInspection[clientstring].Size == _deeperInspection[clientstring].Capacity && + (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _deeperInspection[clientstring].Get()) < + _options.RequestTimeSpan.TotalMilliseconds)) + { + //Looks like we're over the limit + _lockSlim.EnterWriteLock(); + if (!_tempBlocked.ContainsKey(clientstring)) + _tempBlocked.Add(clientstring, Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds); + else + _tempBlocked[clientstring] = Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds; + _lockSlim.ExitWriteLock(); + + m_log.WarnFormat("[{0}]: client: {1} is blocked for {2} milliseconds, X-ForwardedForAllowed status is {3}, endpoint:{4}",_options.ReportingName,clientstring,_options.ForgetTimeSpan.TotalMilliseconds, _options.AllowXForwardedFor, client.Address); + + return false; + } + //else + // return true; + } + else + { + _deeperInspection.Add(clientstring, new CircularBuffer(_options.MaxRequestsInTimeframe + 1, true)); + _deeperInspection[clientstring].Put(Util.EnvironmentTickCount()); + _forgetTimer.Enabled = true; + } + + } + return true; + } + private string GetClientString(XmlRpcRequest request, IPEndPoint client) + { + string clientstring; + if (_options.AllowXForwardedFor && request.Params.Count > 3) + { + object headerstr = request.Params[3]; + if (headerstr != null && !string.IsNullOrEmpty(headerstr.ToString())) + clientstring = request.Params[3].ToString(); + else + clientstring = client.Address.ToString(); + } + else + clientstring = client.Address.ToString(); + return clientstring; + } + + } + + public class BasicDosProtectorOptions + { + public int MaxRequestsInTimeframe; + public TimeSpan RequestTimeSpan; + public TimeSpan ForgetTimeSpan; + public bool AllowXForwardedFor; + public string ReportingName = "BASICDOSPROTECTOR"; + public ThrottleAction ThrottledAction = ThrottleAction.DoThrottledMethod; + } +} diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsRequestHandler.cs b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsRequestHandler.cs index 21166053a0..ed4b2053ab 100644 --- a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsRequestHandler.cs +++ b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsRequestHandler.cs @@ -42,14 +42,22 @@ using log4net; namespace OpenSim.Region.CoreModules.Avatar.Friends { - public class FriendsRequestHandler : BaseStreamHandler + public class FriendsRequestHandler : BaseStreamHandlerBasicDOSProtector { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private FriendsModule m_FriendsModule; public FriendsRequestHandler(FriendsModule fmodule) - : base("POST", "/friends") + : base("POST", "/friends", new BasicDosProtectorOptions() + { + AllowXForwardedFor = true, + ForgetTimeSpan = TimeSpan.FromMinutes(2), + MaxRequestsInTimeframe = 5, + ReportingName = "FRIENDSDOSPROTECTOR", + RequestTimeSpan = TimeSpan.FromSeconds(5), + ThrottledAction = ThrottleAction.DoThrottledMethod + }) { m_FriendsModule = fmodule; } diff --git a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs index a26a5f03e9..0f05e07099 100644 --- a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs +++ b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs @@ -165,7 +165,16 @@ namespace OpenSim.Region.CoreModules.World.WorldMap regionimage = regionimage.Replace("-", ""); m_log.Info("[WORLD MAP]: JPEG Map location: " + m_scene.RegionInfo.ServerURI + "index.php?method=" + regionimage); - MainServer.Instance.AddHTTPHandler(regionimage, OnHTTPGetMapImage); + MainServer.Instance.AddHTTPHandler(regionimage, + new GenericHTTPDOSProtector(OnHTTPGetMapImage, OnHTTPThrottled, new BasicDosProtectorOptions() + { + AllowXForwardedFor = false, + ForgetTimeSpan = TimeSpan.FromMinutes(2), + MaxRequestsInTimeframe = 4, + ReportingName = "MAPDOSPROTECTOR", + RequestTimeSpan = TimeSpan.FromSeconds(10), + ThrottledAction = ThrottleAction.DoThrottledMethod + }).Process); MainServer.Instance.AddLLSDHandler( "/MAP/MapItems/" + m_scene.RegionInfo.RegionHandle.ToString(), HandleRemoteMapItemRequest); @@ -1081,6 +1090,16 @@ namespace OpenSim.Region.CoreModules.World.WorldMap block.Y = (ushort)(r.RegionLocY / Constants.RegionSize); } + public Hashtable OnHTTPThrottled(Hashtable keysvals) + { + Hashtable reply = new Hashtable(); + int statuscode = 500; + reply["str_response_string"] = "I blocked you! HAHAHAHAHAHAHHAHAH"; + reply["int_response_code"] = statuscode; + reply["content_type"] = "text/plain"; + return reply; + } + public Hashtable OnHTTPGetMapImage(Hashtable keysvals) { m_log.Debug("[WORLD MAP]: Sending map image jpeg"); diff --git a/OpenSim/Server/Handlers/Asset/AssetServerGetHandler.cs b/OpenSim/Server/Handlers/Asset/AssetServerGetHandler.cs index 8b23a832ac..0bd02355a5 100644 --- a/OpenSim/Server/Handlers/Asset/AssetServerGetHandler.cs +++ b/OpenSim/Server/Handlers/Asset/AssetServerGetHandler.cs @@ -42,14 +42,22 @@ using OpenSim.Framework.Servers.HttpServer; namespace OpenSim.Server.Handlers.Asset { - public class AssetServerGetHandler : BaseStreamHandler + public class AssetServerGetHandler : BaseStreamHandlerBasicDOSProtector { // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private IAssetService m_AssetService; public AssetServerGetHandler(IAssetService service) : - base("GET", "/assets") + base("GET", "/assets",new BasicDosProtectorOptions() + { + AllowXForwardedFor = true, + ForgetTimeSpan = TimeSpan.FromSeconds(2), + MaxRequestsInTimeframe = 5, + ReportingName = "ASSETGETDOSPROTECTOR", + RequestTimeSpan = TimeSpan.FromSeconds(5), + ThrottledAction = ThrottleAction.DoThrottledMethod + }) { m_AssetService = service; } diff --git a/OpenSim/Server/Handlers/Login/LLLoginHandlers.cs b/OpenSim/Server/Handlers/Login/LLLoginHandlers.cs index e4a0ffa3cd..f2a5678852 100644 --- a/OpenSim/Server/Handlers/Login/LLLoginHandlers.cs +++ b/OpenSim/Server/Handlers/Login/LLLoginHandlers.cs @@ -145,6 +145,17 @@ namespace OpenSim.Server.Handlers.Login return FailedXMLRPCResponse(); } + public XmlRpcResponse HandleXMLRPCLoginBlocked(XmlRpcRequest request, IPEndPoint client) + { + XmlRpcResponse response = new XmlRpcResponse(); + Hashtable resp = new Hashtable(); + + resp["reason"] = "presence"; + resp["message"] = "Logins are currently restricted. Please try again later."; + resp["login"] = "false"; + response.Value = resp; + return response; + } public XmlRpcResponse HandleXMLRPCSetLoginLevel(XmlRpcRequest request, IPEndPoint remoteClient) { diff --git a/OpenSim/Server/Handlers/Login/LLLoginServiceInConnector.cs b/OpenSim/Server/Handlers/Login/LLLoginServiceInConnector.cs index 97e8295690..f60e8923db 100644 --- a/OpenSim/Server/Handlers/Login/LLLoginServiceInConnector.cs +++ b/OpenSim/Server/Handlers/Login/LLLoginServiceInConnector.cs @@ -44,6 +44,7 @@ namespace OpenSim.Server.Handlers.Login private ILoginService m_LoginService; private bool m_Proxy; + private BasicDosProtectorOptions m_DosProtectionOptions; public LLLoginServiceInConnector(IConfigSource config, IHttpServer server, IScene scene) : base(config, server, String.Empty) @@ -88,6 +89,16 @@ namespace OpenSim.Server.Handlers.Login throw new Exception(String.Format("No LocalServiceModule for LoginService in config file")); m_Proxy = serverConfig.GetBoolean("HasProxy", false); + m_DosProtectionOptions = new BasicDosProtectorOptions(); + // Dos Protection Options + m_DosProtectionOptions.AllowXForwardedFor = serverConfig.GetBoolean("DOSAllowXForwardedForHeader", false); + m_DosProtectionOptions.RequestTimeSpan = + TimeSpan.FromMilliseconds(serverConfig.GetInt("DOSRequestTimeFrameMS", 10000)); + m_DosProtectionOptions.MaxRequestsInTimeframe = serverConfig.GetInt("DOSMaxRequestsInTimeFrame", 5); + m_DosProtectionOptions.ForgetTimeSpan = + TimeSpan.FromMilliseconds(serverConfig.GetInt("DOSForgiveClientAfterMS", 120000)); + m_DosProtectionOptions.ReportingName = "LOGINDOSPROTECTION"; + return loginService; } @@ -95,7 +106,9 @@ namespace OpenSim.Server.Handlers.Login private void InitializeHandlers(IHttpServer server) { LLLoginHandlers loginHandlers = new LLLoginHandlers(m_LoginService, m_Proxy); - server.AddXmlRPCHandler("login_to_simulator", loginHandlers.HandleXMLRPCLogin, false); + server.AddXmlRPCHandler("login_to_simulator", + new XmlRpcBasicDOSProtector(loginHandlers.HandleXMLRPCLogin,loginHandlers.HandleXMLRPCLoginBlocked, + m_DosProtectionOptions).Process, false); server.AddXmlRPCHandler("set_login_level", loginHandlers.HandleXMLRPCSetLoginLevel, false); server.SetDefaultLLSDHandler(loginHandlers.HandleLLSDLogin); server.AddWebSocketHandler("/WebSocket/GridLogin", loginHandlers.HandleWebSocketLoginEvents); diff --git a/ThirdPartyLicenses/CircularBuffer.txt b/ThirdPartyLicenses/CircularBuffer.txt new file mode 100644 index 0000000000..09c37d9ff4 --- /dev/null +++ b/ThirdPartyLicenses/CircularBuffer.txt @@ -0,0 +1,16 @@ +Copyright (c) 2012, Alex Regueiro +All rights reserved. +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/bin/Robust.ini.example b/bin/Robust.ini.example index de6fc28cd8..74c208dd60 100644 --- a/bin/Robust.ini.example +++ b/bin/Robust.ini.example @@ -356,6 +356,25 @@ MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnecto ;; 'America/Los_Angeles' is used on Linux/Mac systems whilst 'Pacific Standard Time' is used on Windows DSTZone = "America/Los_Angeles;Pacific Standard Time" + ;Basic Login Service Dos Protection Tweaks + ;; + ;; Some Grids/Users use a transparent proxy that makes use of the X-Forwarded-For HTTP Header, If you do, set this to true + ;; If you set this to true and you don't have a transparent proxy, it may allow attackers to put random things in the X-Forwarded-For header to + ;; get around this basic DOS protection. + ;DOSAllowXForwardedForHeader = false + ;; + ;; The protector adds up requests during this rolling period of time, default 10 seconds + ;DOSRequestTimeFrameMS = 10000 + ;; + ;; The amount of requests in the above timeframe from the same endpoint that triggers protection + ;DOSMaxRequestsInTimeFrame = 5 + ;; + ;; The amount of time that a specific endpoint is blocked. Default 2 minutes. + ;DOSForgiveClientAfterMS = 120000 + ;; + ;; To turn off basic dos protection, set the DOSMaxRequestsInTimeFrame to 0. + + [MapImageService] LocalServiceModule = "OpenSim.Services.MapImageService.dll:MapImageService" ; Set this if you want to change the default diff --git a/bin/config-include/StandaloneCommon.ini.example b/bin/config-include/StandaloneCommon.ini.example index 12c5b95b3b..75fd956d04 100644 --- a/bin/config-include/StandaloneCommon.ini.example +++ b/bin/config-include/StandaloneCommon.ini.example @@ -117,7 +117,7 @@ SRV_AssetServerURI = "http://127.0.0.1:9000" SRV_ProfileServerURI = "http://127.0.0.1:9000" SRV_FriendsServerURI = "http://127.0.0.1:9000" - SRV_IMServerURI = "http://127.0.0.1:9000" + SRV_IMServerURI = "http://127.0.0.1:9000 ;; For Viewer 2 MapTileURL = "http://127.0.0.1:9000/" @@ -150,6 +150,23 @@ ;AllowedClients = "" ;DeniedClients = "" + ; Basic Login Service Dos Protection Tweaks + ; ; + ; ; Some Grids/Users use a transparent proxy that makes use of the X-Forwarded-For HTTP Header, If you do, set this to true + ; ; If you set this to true and you don't have a transparent proxy, it may allow attackers to put random things in the X-Forwarded-For header to + ; ; get around this basic DOS protection. + ; DOSAllowXForwardedForHeader = false + ; ; + ; ; The protector adds up requests during this rolling period of time, default 10 seconds + ; DOSRequestTimeFrameMS = 10000 + ; ; + ; ; The amount of requests in the above timeframe from the same endpoint that triggers protection + ; DOSMaxRequestsInTimeFrame = 5 + ; ; + ; ; The amount of time that a specific endpoint is blocked. Default 2 minutes. + ; DOSForgiveClientAfterMS = 120000 + ; ; + ; ; To turn off basic dos protection, set the DOSMaxRequestsInTimeFrame to 0. [FreeswitchService] ;; If FreeSWITCH is not being used then you don't need to set any of these parameters