diff --git a/blockip/README.md b/blockip/README.md index 91241ee..012b112 100644 --- a/blockip/README.md +++ b/blockip/README.md @@ -5,6 +5,7 @@ ## 特性 - **PAM集成**:自动监控SSH登录失败尝试 +- **连接限速**:防御TCP洪水攻击(每IP每分钟最多20次SSH新连接) - **双栈支持**:完整支持IPv4/IPv6和CIDR网段 - **智能聚合**:自动检测并聚合为更大网段,减少规则数量 - **白名单保护**:白名单规则优先级高于黑名单,保护信任IP diff --git a/blockip/bip b/blockip/bip index 298592f..5e26527 100644 Binary files a/blockip/bip and b/blockip/bip differ diff --git a/blockip/bip-static b/blockip/bip-static index 64a29a6..a456b83 100644 Binary files a/blockip/bip-static and b/blockip/bip-static differ diff --git a/blockip/include/common.h b/blockip/include/common.h index c600b89..8beed3d 100644 --- a/blockip/include/common.h +++ b/blockip/include/common.h @@ -76,4 +76,7 @@ int get_max_retries_from_config(void); /* 保存最大重试次数到配置文件 */ int save_max_retries_to_config(int max_retries); +/* 获取SSH端口 */ +int get_ssh_port(void); + #endif /* COMMON_H */ diff --git a/blockip/src/common.c b/blockip/src/common.c index 3c09338..d09721f 100644 --- a/blockip/src/common.c +++ b/blockip/src/common.c @@ -199,3 +199,59 @@ int save_max_retries_to_config(int max_retries) { return SUCCESS; } + +int get_ssh_port(void) { + int port = 22; /* 默认SSH端口 */ + char line[MAX_LINE_LEN]; + FILE *fp = NULL; + + /* 方法1: ss 匹配ssh相关进程 */ + fp = popen("ss -tlnp 2>/dev/null | grep -E 'sshd|dropbear' | awk '{print $4}' | head -1", "r"); + if (fp) { + if (fgets(line, sizeof(line), fp)) { + char *colon = strrchr(line, ':'); + if (colon) { + int p = atoi(colon + 1); + if (p > 0 && p < 65536) { + pclose(fp); + return p; + } + } + } + pclose(fp); + } + + /* 方法2: netstat 匹配ssh相关进程 */ + fp = popen("netstat -tlnp 2>/dev/null | grep -E 'sshd|dropbear' | awk '{print $4}' | head -1", "r"); + if (fp) { + if (fgets(line, sizeof(line), fp)) { + char *colon = strrchr(line, ':'); + if (colon) { + int p = atoi(colon + 1); + if (p > 0 && p < 65536) { + pclose(fp); + return p; + } + } + } + pclose(fp); + } + + /* 方法3: lsof 匹配ssh相关进程 */ + fp = popen("lsof -iTCP -sTCP:LISTEN -P -n 2>/dev/null | grep -E 'sshd|dropbear' | awk '{print $9}' | head -1", "r"); + if (fp) { + if (fgets(line, sizeof(line), fp)) { + char *colon = strrchr(line, ':'); + if (colon) { + int p = atoi(colon + 1); + if (p > 0 && p < 65536) { + pclose(fp); + return p; + } + } + } + pclose(fp); + } + + return port; +} diff --git a/blockip/src/nftables.c b/blockip/src/nftables.c index b0e3867..0f3e555 100644 --- a/blockip/src/nftables.c +++ b/blockip/src/nftables.c @@ -95,6 +95,15 @@ int init_nftables_rules(void) { NFT_TABLE, NFT_WHITELIST, NFT_TABLE, NFT_WHITELIST); system(command); + /* 1b. SSH连接速率限制(每IP每分钟最多20次新连接,防止TCP洪水) */ + int ssh_port = get_ssh_port(); + snprintf(command, sizeof(command), + "nft list chain %s input | grep -q 'limit rate' || " + "nft add rule %s input tcp dport %d ct state new " + "meter ssh-ratelimit '{ ip saddr limit rate over 20/minute burst 5 packets }' drop", + NFT_TABLE, NFT_TABLE, ssh_port); + system(command); + /* 2. IPv6白名单 accept */ snprintf(command, sizeof(command), "nft list chain %s input | grep -q '@%s' || nft add rule %s input ip6 saddr @%s accept",