🧱 Ban subnets using Windows Firewall rules after they make enough incorrect login attempts, as indicated by Windows Event Log records.
APACHE-2.0 License
Fail2Ban4Win is a background service that temporarily blocks IP ranges in Windows Firewall when enough authentication errors appear in Event Log in a given time period for those IP ranges.
You can customize the duration of the ban, the type of Event Log events to detect, and other options. The example configuration file will set Fail2Ban4Win to ban a /24 subnet for 24 hours after 10 failures to authenticate to either Remote Desktop Services or sshd.
You can customize most of the above specifics.
Fail2Ban4Win.zip
).C:\Program Files (x86)\Fail2Ban4Win\
.Set-ExecutionPolicy RemoteSigned -Scope Process -Force
& 'C:\Program Files (x86)\Fail2Ban4Win\Install Service.ps1'
Fail2Ban4Win.zip
).Fail2Ban4Win.exe
from the ZIP file to the installation directory.services.msc
(GUI), Restart-Service Fail2Ban4Win
(PowerShell), or net stop Fail2Ban4Win & net start Fail2Ban4Win
(Command Prompt).The provided example configuration file has selectors for Remote Desktop Services, Cygwin OpenSSH sshd (updated in 1.3.1), and Windows OpenSSH sshd (updated in 1.3.1). It also has some example values for neverBanSubnets
and other properties that you can replace with your own values.
Be aware that isDryRun
defaults to true
to avoid accidentally blocking traffic until you're ready.
configuration.json
file from the Fail2Ban4Win installation directory in a text editor. (You may need to start the editor elevated depending on your file permissions.)Property name | Default | Description |
---|---|---|
isDryRun |
true |
Firewall rules will only be created or deleted when this is false . |
maxAllowedFailures |
9 |
If an IP range (of size banSubnetBits ) exceeds this number of failures during the failureWindow , it will be banned. |
failureWindow |
1.00:00:00 (1 day) |
How long to consider auth failures. By default, 10 failures in 1 day results in a ban. The format is d.hh:mm:ss . |
banPeriod |
1.00:00:00 (1 day) |
After enough failures, the IP range will be banned by adding a Windows Firewall block rule, which will be removed after this period of time. The format is d.hh:mm:ss . |
banSubnetBits |
0 |
Optional CIDR subnet aggregation size when counting failures and blocking traffic. The example value of 8 bits blocks the /24 subnet, or 255.255.255.0. You can restrict blocking only to the exact IP address by setting this to 0 , which is equivalent to /32. |
banRepeatedOffenseCoefficient |
0.0 |
How much of the banPeriod to add on subsequent offenses (optional). The default banPeriod of 1 day and example coefficient of 1.0 results in a 1 day ban for first offenders, 2 days for 2nd offenders, 3 days for 3rd offenders, and 4 days for 4th offenders or greater. Changing this coefficient from 1.0 to 2.0 would result in successive ban durations of 1 day, 3 days, 5 days, and 7 days instead. |
banRepeatedOffenseMax |
4 |
An optional limit on how many repeated offenses can be used to calculate ban duration. By default, the 5th offense and subsequent bans will be capped at the same duration as the 4th offense ban, which is 4 days. |
logLevel |
Info |
Optionally adjust the logging verbosity of Fail2Ban4Win. Valid values are Trace (most verbose), Debug , Info , Warn , Error , and Fatal (least verbose). All messages at the given level will be logged, as well as all messages at less verbose levels, i.e. Warn will also log Error and Fatal messages. To see the log output, you must run Fail2Ban4Win.exe in a console like Command Prompt or PowerShell. |
neverBanSubnets |
[] |
Optional whitelist of IP ranges that should never be banned, regardless of how many auth failures they generate. Each item can be a single IP address, like 67.210.32.33 , or a range, like 67.210.32.0/24 . |
neverBanReservedSubnets |
true |
By default, IP addresses in the reserved blocks 10.0.0.0/8 , 172.16.0.0/12 , and 192.168.0.0/16 will not be banned, to avoid unintentionally blocking LAN access. To allow all three ranges to be banned, change this to false . To then selectively prevent some of those ranges from getting banned, you may add them to the neverBanSubnets list above. Regardless of this configuration, the loopback address will never be banned. |
eventLogSelectors |
[] |
Required list of events to listen for in Event Log. Each object in the list can have the following properties.logName : required, which log in Event Viewer contains the events, e.g. Application , Security , OpenSSH/Operational .eventId : required, numeric ID of event logged on auth failure, e.g. 4625 for RDP auth errors.source : optional Source, AKA Provider Name, of events, e.g. sshd-session for Cygwin OpenSSH sshd. If omitted, events will not be filtered by Source.ipAddressEventDataName : optional, the Name of the Data element in the event XML's EventData in which to search for the client IP address of the auth request, e.g. IpAddress for RDP. If omitted, the first Data element will be searched instead.ipAddressEventDataIndex : optional, the 0-indexed offset of the Data element in the XML's EventData in which to search for the client IP address, e.g. 3 to search for IP addresses in the fourth Data element in EventData . Useful if EventData has multiple Data children, but none of them have a Name attribute to specify in ipAddressEventDataName , and the IP address doesn't appear in the first one. This offset is applied after any Name attribute filtering, and applies whether or not ipAddressEventDataName is specified. If omitted, defaults to 0 .ipAddressPattern : optional, regular expression pattern string that matches the IP address in the Data element specified above. Useful if you want to filter out some events from the log with the desired ID and source but that don't describe an auth failure (e.g. sshd's disconnect events). If omitted, searches for all IPv4 addresses in the Data element's text content. To set options like case-insensitivity, put (?i) at the start of the pattern. Patterns are not anchored to the entire input string unless you surround them with ^ and $ . If you specify a pattern, ensure the desired IPv4 capture group in your pattern has the name ipAddress , e.g. Failed: (?<ipAddress>(?:\d{1,3}\.){3}\d{1,3})eventPredicate : optional, XPath 1.0 query fragment to filter events based on arbitrary elements, matched against the <Event> element. Useful if not all events with the given logName , eventId , and source should trigger bans, like IIS HTTP 200 responses, e.g. [EventData/Data[@Name='sc-status']=403] . Most XPath functions are not supported by Windows ETW.See Handling a new event below for a tutorial on creating this object. |
services.msc
(GUI), Restart-Service Fail2Ban4Win
(PowerShell), or net stop Fail2Ban4Win & net start Fail2Ban4Win
(Command Prompt) for your changes to take effect. Note that the service will clear existing bans when it starts.In this example, we will go through the process of creating an event for Windows OpenSSH sshd. This event is already supported in the example configuration file, but the following process covers all of the necessary steps to add any other event.
explorer.exe ms-settings:optionalfeatures
or go to Settings › Apps › Apps & features › Manage optional features.eventvwr.msc
).logName
value of the event log selector object comes from the Log Name shown here, in this case, OpenSSH/Operational
.source
value comes from the Source shown here, in this case, OpenSSH
. You can also omit source
in this case because all events in this log have the same Source.eventId
value comes from the Event ID shown here, in this case, 4
.<EventData>
.<Data Name="payload">Failed password for invalid user foo bar from 192.168.1.7 port 49721 ssh2</Data>
ipAddressEventDataName
value comes from the <Data>
element that contains the IP address in its text content. In this case, that element has the Name
attribute value of payload
.
<Data>
element with no Name
attribute, you would omit the ipAddressEventDataName
property from the event log selector object.<Data>
elements with no Name
attributes, you would omit the ipAddressEventDataName
property and set ipAddressEventDataIndex
to the position of the desired Data
element (where the first Data
child of the EventData
element would have index 0).ipAddressPattern
helps narrow down which events represent auth failures. Some events in this log with ID 4 are caused by successful auth attempts or disconnections, which should not trigger firewall bans. By matching the text of an auth failure, the correct events will be processed. The following pattern matches only auth failures and captures the IP address in a named group for processing.
^Failed password for(?: invalid user)? .+ from (?<ipAddress>(?:\d{1,3}\.){3}\d{1,3}) port \d{1,5} ssh\d?$
{
"logName": "OpenSSH/Operational",
"source": "OpenSSH",
"eventId": 4,
"ipAddressEventDataName": "payload",
"ipAddressPattern": "^Failed password for(?: invalid user)? .+ from (?<ipAddress>(?:\\d{1,3}\\.){3}\\d{1,3}) port \\d{1,5} ssh\\d?$"
}
configuration.json
by appending it to the eventLogSelectors
array.Do any of the following.
Fail2Ban4Win
service from the services.msc
GUI.Start-Service Fail2Ban4Win
.net start Fail2Ban4Win
.Fail2Ban4Win.exe
in a console window. This is useful for looking at the log output and verifying your configuration, especially when isDryRun
is true. You can stop the process using Ctrl
+C
.You can see the block rules created by Fail2Ban4Win in Windows Firewall.
wf.msc
).Inbound Rules
.fail2ban
that convinced me that non-stop RDP and SSH login attempts might have a solution.wail2ban
by Katie McLaughlin (glasnt
) for being archived and motivating me to creating my own non-archived implementation.win2ban
for charging twenty-nine American dollars for some cobbled together free open-source projects that made me indignant enough to create my own free, open-source, clean-room implementation.falahati
) for the excellent .NET wrapper for the Windows Firewall COM API.rmustacc
) for talking me out of trying to implement a wait-free list to store failure times and instead continuing to lock array lists.