TL;DR: fail2ban blocks IPs using iptable rules, which isn't strightforward to do on proxied requests
So I went down the rabbit hole trying to solve this issue and
here's what I found.
Disclaimer: I did not manage to solve the issue, and the code and tweaks I've included below should be used with caution. I've never used Perl before and I relied on ChatGPT for sections of it. I would recommend against copying anything below in verbatim.
I found
this proxmox forums thread where the author detailed how they solved the issue of getting client public IP by editing this (/usr/share/perl5/PVE/HTTPServer.pm) file. Their solution didn't work for me since my pve is behind cloudflare and also nginx running on the LAN, so my X-Forwarded-For header contains two IPs instead of just one.
So my solution involved adding the following to line after /usr/share/perl5/PVE/APIServer/AnyEvent.pm:1484 (ProxmoxVE Version 7.4-17):
Perl:
if ($request->header('X-Forwarded-For')) {
my $x_forwarded_for = $request->header('X-Forwarded-For');
# Number of proxies to count from the right
my $proxy_count = 2; # my value is 2 because I use cloudflare, then nginx locally
# Split the input string into an array of IP addresses
my @ip_list = split /\s*,\s*/, $x_forwarded_for;
# Calculate the maximum iteration count
my $max_iterations = $proxy_count + 1;
# Iterate through the IP addresses from the right
for my $i (reverse 0 .. $#ip_list) {
$max_iterations--;
if ($max_iterations == 0) {
last;
}
my $ip = $ip_list[$i];
my $ip_parsed = Net::IP->new($ip);
$proxy_count--;
# Validate the IP address format
if ($ip_parsed) {
if ($proxy_count == 0) {
if($ip_parsed->version() == 4){
my $ipv6_mapped_address = "::ffff:$ip";
$reqstate->{peer_host} = $ipv6_mapped_address;
}
else{
$reqstate->{peer_host} = $ip;
}
last;
}
} else {
warn "Malformed IP found in X-Forwarded-For header. One of your proxies are appending the wrong IP, or \$proxy_count is incorrect.\n";
next;
}
}
if($max_iterations == 0){
warn "Error: Not enough valid IPs in X-Forwarded-For header. One of your proxies are appending the wrong IP, or \$proxy_count is incorrect.\n";
}
}
After editing the file, I did `systemctl restart pvedaemon` and `systemctl restart pveproxy`.
(Note: if you attempt anything similar and there are Perl compilation errors, the two commands above will fail. You can see the errors using `journalctl -xe`)
After this, the IP addresses in /var/log/daemon.log was correct, and I was able to confirm that fail2ban was banning the correct IP addresses, as `fail2ban-client banned` was correctly showing the public IP of my clients.
However, my clients were still able to access the Web UI while on the ban list.
I only found out after I had done all the above, but fail2ban bans users using iptables. So the actual ban cannot be done based on X-Forwarded-For (at least, not using the fail2ban default configuration). So in this configuration fail2ban will ban a specific bad acting client's public IP, and add this IP to the iptables blocklist, but this rule will never be met, because all public traffic is coming to the system from the reverse proxy LAN IP.
Since I'm using cloudflare, some googling shows that there are ways to use fail2ban to execute a script contacts the cloudflare API to block clients. Might pursue this in the future, but for now my understanding is that blocking proxied requests directly on the proxmox system isn't possible.