Only track bannable (globally-routable) source IPs
The ban logic is per source IP, so it only works where the daemon can see the real client. Behind Docker's default bridge networking every client is SNAT'd to the bridge gateway (a 172.16/12 address), so a single IP would stand in for the whole internet -- counting offenses against it would block everyone at once. Add is_bannable_address(): only globally-routable unicast addresses are tracked. Loopback, RFC1918 private, CGNAT (100.64/10), link-local, IPv6 unique-local, and multicast all return false. main.cpp decides trackability from the accepted endpoint and skips both the block check and offense recording for non-global sources. Net effect: banning works where the real IP is visible (FreeBSD jail via pf rdr; Docker with host networking) and is inert -- not catastrophic -- where it is not (Docker bridge). Document the Docker client-IP caveat: docker-compose.yml now defaults to host networking, with the rationale and alternatives in DOCKER.md.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
#include "ban.hpp"
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
@@ -83,6 +84,41 @@ TEST(BanTracker, RespectsCustomConfig) {
|
||||
EXPECT_FALSE(bt.is_blocked("9.9.9.9", kBase + 1h + 1min)); // window elapsed
|
||||
}
|
||||
|
||||
static bool bannable(const char *ip) {
|
||||
return is_bannable_address(boost::asio::ip::make_address(ip));
|
||||
}
|
||||
|
||||
TEST(BannableAddress, GlobalIpv4IsBannable) {
|
||||
EXPECT_TRUE(bannable("8.8.8.8"));
|
||||
EXPECT_TRUE(bannable("192.184.167.198")); // a real scanner seen in the logs
|
||||
EXPECT_TRUE(bannable("1.2.3.4"));
|
||||
}
|
||||
|
||||
TEST(BannableAddress, PrivateAndLocalIpv4AreNotBannable) {
|
||||
EXPECT_FALSE(bannable("127.0.0.1")); // loopback
|
||||
EXPECT_FALSE(bannable("10.1.2.3")); // 10/8
|
||||
EXPECT_FALSE(bannable("172.20.0.1")); // Docker bridge gateway (172.16/12)
|
||||
EXPECT_FALSE(bannable("172.31.255.1"));
|
||||
EXPECT_FALSE(bannable("192.168.1.104")); // the finger jail's own LAN IP
|
||||
EXPECT_FALSE(bannable("169.254.10.1")); // link-local
|
||||
EXPECT_FALSE(bannable("224.0.0.1")); // multicast
|
||||
}
|
||||
|
||||
TEST(BannableAddress, CgnatRangeIsNotBannable) {
|
||||
EXPECT_FALSE(bannable("100.64.0.1")); // bottom of 100.64/10 (CGNAT/Tailscale)
|
||||
EXPECT_FALSE(bannable("100.127.255.1")); // top of the range
|
||||
EXPECT_TRUE(bannable("100.63.255.1")); // just below the range -> public
|
||||
EXPECT_TRUE(bannable("100.128.0.1")); // just above the range -> public
|
||||
}
|
||||
|
||||
TEST(BannableAddress, Ipv6Classification) {
|
||||
EXPECT_TRUE(bannable("2001:4860:4860::8888")); // global
|
||||
EXPECT_FALSE(bannable("::1")); // loopback
|
||||
EXPECT_FALSE(bannable("fe80::1")); // link-local
|
||||
EXPECT_FALSE(bannable("fc00::1")); // unique-local
|
||||
EXPECT_FALSE(bannable("fd12:3456::1")); // unique-local
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
|
||||
Reference in New Issue
Block a user