We are using haproxy for token redirected load balancing of a Remote Desktop Session Hosts farm and because the farm is exposed on the default RDP port (3389) it is subjected to constant brute-force attacks. These attacks besides being a nuisance do slow down the terminal servers and cause Remote Desktop Services Manager to constantly refresh users list which annoys support people even more.
How we use fail2ban to block IPs based on failed logon attempts
First we created a simple monitoring script on the terminal servers which listens for failed logon attempts and transfers these to a log file (/var/log/rdp/audit.log) on the gateway machine where the haproxy is installed. The monitoring script is based on ts_block script by EvanAnderson but instead of blocking IPs using the local firewall it uses plink.exe to append log entries to /var/log/rdp/audit.log on the gateway.
The script logs three types of entries based on whether the user is recognized (from local Remote Desktop Users group), whether it’s strictly denied (most brute-forced users like Admin, Administrator, etc.) and whether it’s unknown (not in Remote Desktop Users group). This allows fail2ban to organize two jails: a soft one for recognized users (ban for 10 minutes) and a hard one for unknown/denied users (ban for a week). The format of the entries includes date/time, process name/id and originating terminal server name so it can be easily parsed and matched by fail2ban.
This is the configuration of the jails in /etc/fail2ban/jail.local
we use
[rdp-hard-iptables] enabled = true filter = rdp-hard action = iptables-multiport[name=rdp-hard, port="3389,3390,3391", protocol=tcp] logpath = /var/log/rdp/audit-hard.log maxretry = 1 bantime = 604800 ; 1 week ignoreip = ... [rdp-soft-iptables] enabled = true filter = rdp-soft action = iptables-multiport[name=rdp-soft, port="3389,3390,3391", protocol=tcp] logpath = /var/log/rdp/audit.log maxretry = 5 ignoreip = ...
The hard one is blocking access to ports 3389, 3390 and 3391 from the source IP for a week on first failed attempt. The soft jail is blocking access after 5 failed attempts for 10 minutes only. Something we found out is that two jails cannot use the same log file. That’s why the second one is using a hard-linked /var/log/rdp/audit-hard.log
instead. You can populate ignoreip
entries with you local subnets (e.g. 192.168.10.0/24) and with legitimate users known static IPs (e.g. 34.56.78.90)
Here is the denied/unknown users matching regular expression in rdp-hard.conf
from /etc/fail2ban/filter.d
# Fail2Ban configuration file # # Author: Wqw # [Definition] # Option: failregex # Notes.: regex to match the password failures messages in the logfile. The # host must be matched by a group named "host". The tag "<HOST>" can # be used for standard IP/hostname matching and is only an alias for # (?:::f{4,6}:)?(?P<host>[\w\-.^_]+) # Values: TEXT # # Sample log: Nov 8 14:55:13 TS03 rdplog.vbs[30296]: Unknown user name admino from 10.10.10.2 # failregex = Unknown user name .* from <HOST>\s*$ Denied user name .* from <HOST>\s*$ # Option: ignoreregex # Notes.: regex to ignore. If this regex matches, the line is ignored. # Values: TEXT # ignoreregex =
And rdp-soft.conf
# Fail2Ban configuration file # # Author: Wqw # [Definition] # Option: failregex # Notes.: regex to match the password failures messages in the logfile. The # host must be matched by a group named "host". The tag "<HOST>" can # be used for standard IP/hostname matching and is only an alias for # (?:::f{4,6}:)?(?P<host>[\w\-.^_]+) # Values: TEXT # # Sample log: Nov 5 17:39:10 TS02 rdplog.vbs[10896]: Login failed for administrator from 88.84.162.198 # failregex = Login failed for .* from <HOST>\s*$ # Option: ignoreregex # Notes.: regex to ignore. If this regex matches, the line is ignored. # Values: TEXT # ignoreregex =
Additionally we modified iptables-multiport.conf
in action.d
to use -m state --state NEW
iptables option so that upon soft ban existing connections are not dropped. We have multiple users NATed behind a single source IP so this modifications prevents existing connection being dropped when someone fails to remember his/her password and blocks their common source IP.
... actionstart = iptables -N fail2ban-<name> iptables -A fail2ban-<name> -j RETURN iptables -I <chain> -m state --state NEW -p <protocol> -m multiport --dports <port> -j fail2ban-<name> ... actionstop = iptables -D <chain> -m state --state NEW -p <protocol> -m multiport --dports <port> -j fail2ban-<name> iptables -F fail2ban-<name> iptables -X fail2ban-<name>
We had some troubles with logrotate on audit.log
. Apparently fail2ban does not recognize when the monitored file is rotated and stops matching entries. Tried copytruncate logrotate option without luck. Tried gamin backed on fail2ban with no success too.
Setting up monitoring script on the terminal servers
The rdplog.vbs
script expects couple of parameters: use /s to specify the gateway server (where sshd and fail2ban are running) use /l for ssh user /pw for password or /i for key file. Optionally use /p for sshd port number if not the standard one.
The easiest way to start the script as unattended service on the Windows boxes is by using Task Scheduler. Just create a task for cscript.exe
with parameters //nologo {full_path}\rdplog.vbs /s:{ssh_server} /l:{ssh_user} /i:{key_file.ppk}
and with with At system startup trigger. This task can be started manually too.
To test the script just start it in a cmd console with cscript.exe //nologo rdplog.vbs /s:...
. Note that the script shells plink.exe to connect to ssh server and its executable must be available in local folder or reachable in a folder in PATH.
Notes on SSL connections
Newest RDP clients use SSL to encrypt client connections to RDSH servers. It turns out that only connections with RDP Security do log the source IP in the system’s security log. There is no work-around for this problem, besides lowering the TCP listener encryption on the RDSH servers to RDP Security (which works but is not recommended). Still, our fail2ban solution does an excellent job because most of the brute-force attacks don’t use the more CPU intensive SSL connections yet.
Links