Skip to content

Commit

Permalink
https://ipthreat.net integration
Browse files Browse the repository at this point in the history
  • Loading branch information
jjxtra committed Sep 2, 2022
1 parent b197a94 commit 3ad29f4
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 20 deletions.
8 changes: 8 additions & 0 deletions IPBanCore/Core/IPBan/IPBanConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ public void Dispose()
private readonly TimeSpan cycleTime = TimeSpan.FromMinutes(1.0d);
private readonly TimeSpan minimumTimeBetweenFailedLoginAttempts = TimeSpan.FromSeconds(5.0);
private readonly TimeSpan minimumTimeBetweenSuccessfulLoginAttempts = TimeSpan.FromSeconds(5.0);

private readonly string ipThreatApiKey = string.Empty;
private readonly int failedLoginAttemptsBeforeBan = 5;
private readonly bool resetFailedLoginCountForUnbannedIPAddresses;
private readonly string firewallRulePrefix = "IPBan_";
Expand Down Expand Up @@ -180,6 +182,7 @@ private IPBanConfig(XmlDocument doc, IDnsLookup dns = null, IDnsServerList dnsLi
appSettings[node.Attributes["key"].Value] = node.Attributes["value"].Value;
}

GetConfig<string>("IPThreatApiKey", ref ipThreatApiKey);
GetConfig<int>("FailedLoginAttemptsBeforeBan", ref failedLoginAttemptsBeforeBan, 1, 50);
GetConfig<bool>("ResetFailedLoginCountForUnbannedIPAddresses", ref resetFailedLoginCountForUnbannedIPAddresses);
GetConfigArray<TimeSpan>("BanTime", ref banTimes, emptyTimeSpanArray);
Expand Down Expand Up @@ -881,6 +884,11 @@ appSettingsOverride is not null &&
/// </summary>
public IReadOnlyDictionary<string, string> AppSettings => appSettings;

/// <summary>
/// Api key from https://ipthreat.net, if any
/// </summary>
public string IPThreatApiKey { get { return ipThreatApiKey; } }

/// <summary>
/// Number of failed login attempts before a ban is initiated
/// </summary>
Expand Down
129 changes: 129 additions & 0 deletions IPBanCore/Core/IPBan/IPBanIPThreatUploader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System.Reflection;
using System.Diagnostics.CodeAnalysis;

namespace DigitalRuby.IPBanCore;

/// <summary>
/// Sync failed logins to ipthreat api
/// </summary>
public sealed class IPBanIPThreatUploader : IUpdater, IIPAddressEventHandler
{
private static readonly Uri ipThreatReportApiUri = new("https://api.ipthreat.net/api/bulkreport");

private readonly IIPBanService service;
private readonly Random random = new();
private readonly List<IPAddressLogEvent> events = new();

private DateTime nextRun;

/// <summary>
/// Constructor
/// </summary>
/// <param name="service">Service</param>
public IPBanIPThreatUploader(IIPBanService service)
{
this.service = service;
nextRun = IPBanService.UtcNow;//.AddMinutes(random.Next(30, 91));
}

/// <inheritdoc />
public void Dispose()
{

}

/// <inheritdoc />
public async Task Update(CancellationToken cancelToken = default)
{
// ready to run?
var now = IPBanService.UtcNow;
if (now < nextRun)
{
return;
}

// do we have an api key?
var apiKey = (service.Config.IPThreatApiKey ?? string.Empty).Trim();
if (string.IsNullOrWhiteSpace(apiKey))
{
return;
}
// api key can be read from env var if starts and ends with %
else if (apiKey.StartsWith("%") && apiKey.EndsWith("%"))
{
apiKey = Environment.GetEnvironmentVariable(apiKey.Trim('%'));
if (string.IsNullOrWhiteSpace(apiKey))
{
return;
}
}

// copy events
IReadOnlyCollection<IPAddressLogEvent> eventsCopy;
lock (events)
{
eventsCopy = events.ToArray();
events.Clear();
}
if (eventsCopy.Count == 0)
{
return;
}

// post json
try
{
/*
[{
"ip": "1.2.3.4",
"flags": "None",
"system": "SMTP",
"notes": "Failed password",
"ts": "2022-09-02T15:24:07.842Z",
"count": 1
}]
*/
var transform =
eventsCopy.Select(e => new
{
ip = e.IPAddress,
flags = "BruteForce",
system = e.Source,
notes = "ipban " + Assembly.GetEntryAssembly()?.GetName().Version?.ToString(3),
ts = e.Timestamp.ToString("s", CultureInfo.InvariantCulture) + "Z",
count = e.Count
});
var jsonObj = new { items = transform };
// have to use newtonsoft here
var postJson = System.Text.Encoding.UTF8.GetBytes(Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj));
await service.RequestMaker.MakeRequestAsync(ipThreatReportApiUri,
postJson,
new KeyValuePair<string, object>[] { new KeyValuePair<string, object>("X-API-KEY", apiKey) },
cancelToken);
}
catch (Exception ex)
{
Logger.Error(ex, "Failed to post json to ipthreat api, please double check your IPThreatApiKey setting");
}

// set next run time
nextRun = now.AddMinutes(random.Next(30, 91));
}

/// <inheritdoc />
public void AddIPAddressLogEvents(IEnumerable<IPAddressLogEvent> events)
{
lock (events)
{
this.events.AddRange(events.Where(e => e.Type == IPAddressEventType.FailedLogin &&
!service.Config.IsWhitelisted(e.IPAddress)));
}
}
}
2 changes: 2 additions & 0 deletions IPBanCore/Core/IPBan/IPBanService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ public void AddIPAddressLogEvents(IEnumerable<IPAddressLogEvent> events)
{
pendingLogEvents.AddRange(eventsArray);
}
IPThreatUploader.AddIPAddressLogEvents(eventsArray);
}

/// <summary>
Expand Down Expand Up @@ -374,6 +375,7 @@ public async Task RunAsync(CancellationToken cancelToken)
AddUpdater(new IPBanUnblockIPAddressesUpdater(this, Path.Combine(AppContext.BaseDirectory, "unban*.txt")));
AddUpdater(new IPBanBlockIPAddressesUpdater(this, Path.Combine(AppContext.BaseDirectory, "ban*.txt")));
AddUpdater(DnsList);
AddUpdater(IPThreatUploader ??= new IPBanIPThreatUploader(this));

// start delegate if we have one
IPBanDelegate?.Start(this);
Expand Down
5 changes: 5 additions & 0 deletions IPBanCore/Core/IPBan/IPBanService_Fields.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ public string ConfigFilePath
/// </summary>
public IDnsServerList DnsList { get; set; } = new IPBanDnsServerList();

/// <summary>
/// If an api key is specified in the IPThreatApiKey app setting
/// </summary>
public IPBanIPThreatUploader IPThreatUploader { get; set; }

/// <summary>
/// Extra handler for banned ip addresses (optional)
/// </summary>
Expand Down
21 changes: 12 additions & 9 deletions IPBanCore/Core/Interfaces/IHttpRequestMaker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public interface IHttpRequestMaker
/// <param name="headers">Optional http headers</param>
/// <param name="cancelToken">Cancel token</param>
/// <returns>Task of response byte[]</returns>
Task<byte[]> MakeRequestAsync(Uri uri, string postJson = null, IEnumerable<KeyValuePair<string, object>> headers = null,
Task<byte[]> MakeRequestAsync(Uri uri, byte[] postJson = null, IEnumerable<KeyValuePair<string, object>> headers = null,
CancellationToken cancelToken = default) => throw new NotImplementedException();
}

Expand Down Expand Up @@ -83,7 +83,7 @@ public class DefaultHttpRequestMaker : IHttpRequestMaker
public static long LocalRequestCount { get { return localRequestCount; } }

/// <inheritdoc />
public async Task<byte[]> MakeRequestAsync(Uri uri, string postJson = null, IEnumerable<KeyValuePair<string, object>> headers = null,
public async Task<byte[]> MakeRequestAsync(Uri uri, byte[] postJson = null, IEnumerable<KeyValuePair<string, object>> headers = null,
CancellationToken cancelToken = default)
{
if (uri.Host.StartsWith("localhost", StringComparison.OrdinalIgnoreCase) ||
Expand Down Expand Up @@ -122,15 +122,16 @@ public async Task<byte[]> MakeRequestAsync(Uri uri, string postJson = null, IEnu
}
}
byte[] response;
if (string.IsNullOrWhiteSpace(postJson))
if (postJson is null || postJson.Length == 0)
{
msg.Method = HttpMethod.Get;
}
else
{
msg.Headers.Add("Cache-Control", "no-cache");
msg.Method = HttpMethod.Post;
msg.Content = new StringContent(postJson, Encoding.UTF8, "application/json");
msg.Headers.Add("Cache-Control", "no-cache");
msg.Content = new ByteArrayContent(postJson);
msg.Content.Headers.Add("Content-Type", "application/json; charset=utf-8");
}

var responseMsg = await client.SendAsync(msg, cancelToken);
Expand All @@ -139,17 +140,19 @@ public async Task<byte[]> MakeRequestAsync(Uri uri, string postJson = null, IEnu
{
throw new WebException("Request to url " + uri + " failed, status: " + responseMsg.StatusCode + ", response: " + Encoding.UTF8.GetString(response));
}
if (uri.AbsolutePath.EndsWith(".gz", StringComparison.OrdinalIgnoreCase))
else if (response is not null &&
response.Length != 0 &&
uri.AbsolutePath.EndsWith(".gz", StringComparison.OrdinalIgnoreCase))
{
try
{
// in case response somehow got gzip decompressed already, catch exception and keep response as is
MemoryStream decompressdStream = new();
MemoryStream decompressStream = new();
{
using GZipStream gz = new(new MemoryStream(response), CompressionMode.Decompress, true);
gz.CopyTo(decompressdStream);
gz.CopyTo(decompressStream);
}
response = decompressdStream.ToArray();
response = decompressStream.ToArray();
}
catch
{
Expand Down
6 changes: 3 additions & 3 deletions IPBanCore/IPBanCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
<PreserveCompilationContext>true</PreserveCompilationContext>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<PackageId>DigitalRuby.IPBanCore</PackageId>
<Version>1.7.4</Version>
<AssemblyVersion>1.7.4</AssemblyVersion>
<FileVersion>1.7.4</FileVersion>
<Version>1.8.0</Version>
<AssemblyVersion>1.8.0</AssemblyVersion>
<FileVersion>1.8.0</FileVersion>
<Authors>Jeff Johnson</Authors>
<Company>Digital Ruby, LLC</Company>
<Product>IPBan</Product>
Expand Down
10 changes: 10 additions & 0 deletions IPBanCore/ipban.config
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,16 @@ Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON'. Reason: Could not find a l

<appSettings>

<!--
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
2] Go to https://ipthreat.net/requestpermissions to get bulk report permission
3] Once you get an email back verifying permissions, create an api key at https://ipthreat.net/account?tab=apikeys
4] Enter your api key below (no need to restart the service)
Note: You can use %env_var_name% to read the api key from an environment variable to avoid exposing it in the config
-->
<add key="IPThreatApiKey" value="" />

<!-- Number of failed logins before banning the ip address -->
<add key="FailedLoginAttemptsBeforeBan" value="5"/>

Expand Down
2 changes: 1 addition & 1 deletion IPBanTests/IPBanUriFirewallRuleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ private async Task TestFileInternal(string uri)
}
}

public Task<byte[]> MakeRequestAsync(Uri uri, string postJson = null, IEnumerable<KeyValuePair<string, object>> headers = null,
public Task<byte[]> MakeRequestAsync(Uri uri, byte[] postJson = null, IEnumerable<KeyValuePair<string, object>> headers = null,
CancellationToken cancelToken = default)
{
return Task.FromResult(Encoding.UTF8.GetBytes(GetTestFile()));
Expand Down
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
IPBan - Block out attackers quickly and easily on Linux and Windows
-----
[![Github Sponsorship](.github/github_sponsor_btn.svg)](https://github.com/sponsors/jjxtra)

[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7EJ3K33SRLU9E)

[![Build Status](https://dev.azure.com/DigitalRuby/DigitalRuby/_apis/build/status/DigitalRuby_IPBan?branchName=master)](https://dev.azure.com/DigitalRuby/DigitalRuby/_build/latest?definitionId=4&branchName=master)

Get a discount on IPBan Pro by visiting <a href='https://ipban.com/upgrade-to-ipban-pro/'>https://ipban.com/upgrade-to-ipban-pro/</a>.

You can also visit the ipban discord at https://discord.gg/GRmbCcKFNR to chat with other IPBan users.

<a href="https://ipban.com/newsletter">Sign up for the IPBan Mailing List</a>
**Helpful Links**
- Get a discount on IPBan Pro by visiting <a href='https://ipban.com/upgrade-to-ipban-pro/'>https://ipban.com/upgrade-to-ipban-pro/</a>.
- <a href='https://ipthreat.net/integrations/ipban'>Integrate IPBan with IPThreat</a>, a 100% free to use website and service. Unlike some other sites and services, we don't charge a high subscription fee. Keeping your servers protected should not cost a fortune.
- You can also visit the ipban discord at https://discord.gg/GRmbCcKFNR to chat with other IPBan users.
- <a href="https://ipban.com/newsletter">Sign up for the IPBan Mailing List</a>

**Requirements**
- IPBan requires .NET 6 SDK to build and debug code. For an IDE, I suggest Visual Studio Community for Windows, or VS code for Linux. All are free. You can build a self contained executable to eliminate the need for dotnet core on the server machine, or just download the precompiled binaries.
Expand Down Expand Up @@ -75,6 +73,10 @@ To disable anonymously sending banned ip addresses to the global ipban database,

Get a discount on IPBan Pro by visiting <a href='https://ipban.com/upgrade-to-ipban-pro/'>https://ipban.com/upgrade-to-ipban-pro/</a>.

**Other Services**

<a href='https://ipthreat.net/integrations/ipban'>Integrate IPBan with IPThreat</a>, a 100% free to use website and service. Unlike some other sites and services, we don't charge high subscription fee. Keeping your servers protected should not cost a fortune.

**Dontations**
If the free IPBan has helped you and you feel so inclined, please consider donating...

Expand Down

0 comments on commit 3ad29f4

Please sign in to comment.