Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions IPBanCore/Core/IPBan/IPBanConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ public void Dispose()
private readonly TimeSpan minimumTimeBetweenSuccessfulLoginAttempts = TimeSpan.FromSeconds(5.0);

private readonly string ipThreatApiKey = string.Empty;
private readonly string proxyAddress = string.Empty;
private readonly string proxyUserName = string.Empty;
private readonly string proxyPassword = string.Empty;
private readonly bool proxyServiceUrls;
private readonly int failedLoginAttemptsBeforeBan = 5;
private readonly bool resetFailedLoginCountForUnbannedIPAddresses;
private readonly string firewallRulePrefix = "IPBan_";
Expand Down Expand Up @@ -185,6 +189,10 @@ private IPBanConfig(XmlDocument doc, IDnsLookup dns = null, IDnsServerList dnsLi
}

TryGetConfig<string>("IPThreatApiKey", ref ipThreatApiKey, false);
TryGetConfig<string>("ProxyAddress", ref proxyAddress, false);
TryGetConfig<string>("ProxyUserName", ref proxyUserName, false);
TryGetConfig<string>("ProxyPassword", ref proxyPassword, false);
TryGetConfig<bool>("ProxyServiceUrls", ref proxyServiceUrls);
GetConfig<int>("FailedLoginAttemptsBeforeBan", ref failedLoginAttemptsBeforeBan, 1, 50);
TryGetConfig<bool>("ResetFailedLoginCountForUnbannedIPAddresses", ref resetFailedLoginCountForUnbannedIPAddresses);
GetConfigArray<TimeSpan>("BanTime", ref banTimes, emptyTimeSpanArray);
Expand Down Expand Up @@ -1014,6 +1022,27 @@ public static string ValidateFirewallUriRules(string firewallUriRules)
/// </summary>
public string IPThreatApiKey { get { return ipThreatApiKey; } }

/// <summary>
/// Proxy address for http requests (e.g. http://proxy:8080)
/// </summary>
public string ProxyAddress { get { return proxyAddress; } }

/// <summary>
/// Proxy user name, if required
/// </summary>
public string ProxyUserName { get { return proxyUserName; } }

/// <summary>
/// Proxy password, if required
/// </summary>
public string ProxyPassword { get { return proxyPassword; } }

/// <summary>
/// Whether to use proxy for service URLs (GetUrlUpdate, GetUrlStart, GetUrlStop, GetUrlConfig).
/// Default is false - service URLs will not use proxy.
/// </summary>
public bool ProxyServiceUrls { get { return proxyServiceUrls; } }

/// <summary>
/// Number of failed login attempts before a ban is initiated
/// </summary>
Expand Down
8 changes: 7 additions & 1 deletion IPBanCore/Core/IPBan/IPBanService_Private.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ internal async Task UpdateConfiguration(CancellationToken cancelToken)
// set new config and re-load everything
Config = newConfig;

// update proxy settings for http requests
DefaultHttpRequestMaker.ProxyAddress = newConfig.ProxyAddress;
DefaultHttpRequestMaker.ProxyUserName = newConfig.ProxyUserName;
DefaultHttpRequestMaker.ProxyPassword = newConfig.ProxyPassword;

// load the firewall, detecting a change by referencing the old config
await LoadFirewall(oldConfig);

Expand Down Expand Up @@ -847,7 +852,8 @@ protected virtual async Task<bool> GetUrl(UrlType urlType, CancellationToken can
try
{
KeyValuePair<string, object>[] headers = (Authorization is null ? null : new KeyValuePair<string, object>[] { new("Authorization", Authorization) });
byte[] bytes = await RequestMaker.MakeRequestAsync(new Uri(url), headers: headers, cancelToken: cancelToken);
IHttpRequestMaker requestMaker = (Config.ProxyServiceUrls ? RequestMaker : DirectHttpRequestMaker.Instance);
byte[] bytes = await requestMaker.MakeRequestAsync(new Uri(url), headers: headers, cancelToken: cancelToken);
if (urlType == UrlType.Start)
{
GotStartUrl = true;
Expand Down
90 changes: 89 additions & 1 deletion IPBanCore/Core/Interfaces/IHttpRequestMaker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,65 @@ Task<byte[]> MakeRequestAsync(Uri uri,
CancellationToken cancelToken = default);
}

/// <summary>
/// Direct http request maker that does not use proxy
/// </summary>
public sealed class DirectHttpRequestMaker : IHttpRequestMaker
{
/// <summary>
/// Singleton of DirectHttpRequestMaker
/// </summary>
public static DirectHttpRequestMaker Instance { get; } = new DirectHttpRequestMaker();

/// <inheritdoc />
public async Task<byte[]> MakeRequestAsync(Uri uri,
byte[] postJson = null,
IEnumerable<KeyValuePair<string, object>> headers = null,
string method = null,
CancellationToken cancelToken = default)
{
Assembly versionAssembly = Assembly.GetEntryAssembly() ?? Assembly.GetAssembly(typeof(IPBanService)) ?? GetType().Assembly;
HttpRequestMessage msg = new()
{
RequestUri = uri
};
msg.Headers.Add("User-Agent", versionAssembly.GetName().Name);
if (headers != null)
{
foreach (KeyValuePair<string, object> header in headers)
{
msg.Headers.Add(header.Key, header.Value.ToHttpHeaderString());
}
}
byte[] response;
if (postJson is null || postJson.Length == 0)
{
msg.Method = HttpMethod.Get;
}
else
{
msg.Method = HttpMethod.Post;
msg.Headers.Add("Cache-Control", "no-cache");
msg.Content = new ByteArrayContent(postJson);
msg.Content.Headers.Add("Content-Type", "application/json; charset=utf-8");
}

if (!string.IsNullOrWhiteSpace(method))
{
msg.Method = new HttpMethod(method);
}

var client = new HttpClient();
var responseMsg = await client.SendAsync(msg, cancelToken);
response = await responseMsg.Content.ReadAsByteArrayAsync(cancelToken);
if (!responseMsg.IsSuccessStatusCode)
{
throw new HttpRequestException("Request to url " + uri + " failed, status: " + responseMsg.StatusCode);
}
return response;
}
}

/// <summary>
/// Default implementation of IHttpRequestMaker
/// </summary>
Expand All @@ -70,6 +129,21 @@ public sealed class DefaultHttpRequestMaker : IHttpRequestMaker
/// </summary>
public static bool DisableLiveRequests { get; set; }

/// <summary>
/// Proxy address for http requests (e.g. http://proxy:8080)
/// </summary>
public static string ProxyAddress { get; set; }

/// <summary>
/// Proxy user name, if required
/// </summary>
public static string ProxyUserName { get; set; }

/// <summary>
/// Proxy password, if required
/// </summary>
public static string ProxyPassword { get; set; }

private static long liveRequestCount;
/// <summary>
/// Global counter of live requests made
Expand Down Expand Up @@ -135,7 +209,21 @@ public async Task<byte[]> MakeRequestAsync(Uri uri,
msg.Method = new HttpMethod(method);
}

var client = new HttpClient();
HttpClient client;
if (!string.IsNullOrWhiteSpace(ProxyAddress))
{
var handler = new HttpClientHandler();
handler.Proxy = new System.Net.WebProxy(ProxyAddress);
if (!string.IsNullOrWhiteSpace(ProxyUserName) && !string.IsNullOrWhiteSpace(ProxyPassword))
{
handler.Proxy.Credentials = new System.Net.NetworkCredential(ProxyUserName, ProxyPassword);
}
client = new HttpClient(handler);
}
else
{
client = new HttpClient();
}
var responseMsg = await client.SendAsync(msg, cancelToken);
response = await responseMsg.Content.ReadAsByteArrayAsync(cancelToken);
if (!responseMsg.IsSuccessStatusCode)
Expand Down
14 changes: 14 additions & 0 deletions IPBanCore/ipban.config
Original file line number Diff line number Diff line change
Expand Up @@ -975,6 +975,20 @@

<!-- Note that all app settings can use value="%[env_var_name]%" to read the value from an environment variable-->

<!--
Proxy settings for http requests (optional)
Use these settings to route http requests through a proxy server.
This will affect all http requests made by IPBan, including IPThreat uploads and firewall URI rules.
-->
<!-- Proxy address in format: http://proxy:port or https://proxy:port -->
<add key="ProxyAddress" value="" />
<!-- Proxy user name, if proxy requires authentication -->
<add key="ProxyUserName" value="" />
<!-- Proxy password, if proxy requires authentication -->
<add key="ProxyPassword" value="" />
<!-- Whether to use proxy for service URLs (GetUrlUpdate, GetUrlStart, GetUrlStop, GetUrlConfig). Default is false. -->
<add key="ProxyServiceUrls" value="false" />

<!--
Enter your https://ipthreat.net api key here to submit failed logins to the 100% free ipthreat site and service
1] Create an account on the ipthreat website : https://ipthreat.net/account/signup
Expand Down
32 changes: 32 additions & 0 deletions IPBanTests/IPBanConfigTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -483,4 +483,36 @@ public void TestFirewallRules_EmptyIpSection_NoCrash()
ClassicAssert.AreEqual(0, r2.AllowPortRanges.Count);
}
}

[TestFixture]
public class IPBanConfigProxyTests
{
[Test]
public void TestProxySettings_ParseFromXml()
{
string configXml = "<?xml version='1.0'?><configuration><appSettings>" +
"<add key='ProxyAddress' value='http://proxy:8080' />" +
"<add key='ProxyUserName' value='user' />" +
"<add key='ProxyPassword' value='pass' />" +
"<add key='ProxyServiceUrls' value='true' />" +
"</appSettings></configuration>";
var cfg = IPBanConfig.LoadFromXml(configXml);

ClassicAssert.AreEqual("http://proxy:8080", cfg.ProxyAddress);
ClassicAssert.AreEqual("user", cfg.ProxyUserName);
ClassicAssert.AreEqual("pass", cfg.ProxyPassword);
ClassicAssert.IsTrue(cfg.ProxyServiceUrls);
}

[Test]
public void TestProxyServiceUrls_DefaultFalse()
{
string configXml = "<?xml version='1.0'?><configuration><appSettings>" +
"<add key='ProxyAddress' value='http://proxy:8080' />" +
"</appSettings></configuration>";
var cfg = IPBanConfig.LoadFromXml(configXml);

ClassicAssert.IsFalse(cfg.ProxyServiceUrls);
}
}
}