This commit is contained in:
BIN
Binary file not shown.
Binary file not shown.
@@ -21,6 +21,8 @@
|
|||||||
#define MAX_LOG_SIZE 10485760 // 10MB
|
#define MAX_LOG_SIZE 10485760 // 10MB
|
||||||
#define DEFAULT_MAX_RETRIES 3
|
#define DEFAULT_MAX_RETRIES 3
|
||||||
#define DEFAULT_BAN_TIME "24h"
|
#define DEFAULT_BAN_TIME "24h"
|
||||||
|
#define DEFAULT_RATE_LIMIT 20
|
||||||
|
#define DEFAULT_RATE_BAN_TIME "10m"
|
||||||
#define RECORD_DIR CONFIG_DIR "/counts"
|
#define RECORD_DIR CONFIG_DIR "/counts"
|
||||||
#define PERSIST_FILE CONFIG_DIR "/blacklist"
|
#define PERSIST_FILE CONFIG_DIR "/blacklist"
|
||||||
#define WHITELIST_FILE CONFIG_DIR "/whitelist"
|
#define WHITELIST_FILE CONFIG_DIR "/whitelist"
|
||||||
@@ -79,4 +81,16 @@ int save_max_retries_to_config(int max_retries);
|
|||||||
/* 获取SSH端口 */
|
/* 获取SSH端口 */
|
||||||
int get_ssh_port(void);
|
int get_ssh_port(void);
|
||||||
|
|
||||||
|
/* 获取SSH端口速率 */
|
||||||
|
int get_rate_limit_from_config(void);
|
||||||
|
|
||||||
|
/* 保存SSH端口速率 */
|
||||||
|
int save_rate_limit_to_config(int rate_limit);
|
||||||
|
|
||||||
|
/* 获取速率限制封禁时间 */
|
||||||
|
const char* get_rate_ban_time_from_config(void);
|
||||||
|
|
||||||
|
/* 保存速率限制封禁时间 */
|
||||||
|
int save_rate_ban_time_to_config(const char *ban_time);
|
||||||
|
|
||||||
#endif /* COMMON_H */
|
#endif /* COMMON_H */
|
||||||
|
|||||||
@@ -255,3 +255,119 @@ int get_ssh_port(void) {
|
|||||||
|
|
||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 通用配置读取函数(整数) */
|
||||||
|
static int get_config_int(const char *key, int default_value, int min_val, int max_val) {
|
||||||
|
FILE *fp = fopen(CONFIG_FILE, "r");
|
||||||
|
if (!fp) return default_value;
|
||||||
|
|
||||||
|
char line[MAX_LINE_LEN];
|
||||||
|
size_t key_len = strlen(key);
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), fp)) {
|
||||||
|
if (line[0] == '#' || line[0] == '\n') continue;
|
||||||
|
if (strncmp(line, key, key_len) == 0 && line[key_len] == '=') {
|
||||||
|
int value = atoi(line + key_len + 1);
|
||||||
|
fclose(fp);
|
||||||
|
return (value >= min_val && value <= max_val) ? value : default_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
return default_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 通用配置读取函数(字符串) */
|
||||||
|
static const char* get_config_str(const char *key, const char *default_value, char *buffer, size_t buf_size) {
|
||||||
|
FILE *fp = fopen(CONFIG_FILE, "r");
|
||||||
|
if (!fp) return default_value;
|
||||||
|
|
||||||
|
char line[MAX_LINE_LEN];
|
||||||
|
size_t key_len = strlen(key);
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), fp)) {
|
||||||
|
if (line[0] == '#' || line[0] == '\n') continue;
|
||||||
|
if (strncmp(line, key, key_len) == 0 && line[key_len] == '=') {
|
||||||
|
char *value = line + key_len + 1;
|
||||||
|
char *p = value;
|
||||||
|
while (*p && *p != '\n' && *p != '\r') p++;
|
||||||
|
*p = '\0';
|
||||||
|
while (*value == ' ' || *value == '\t') value++;
|
||||||
|
|
||||||
|
size_t len = strlen(value);
|
||||||
|
if (len > 0) {
|
||||||
|
if (len >= buf_size) len = buf_size - 1;
|
||||||
|
memcpy(buffer, value, len);
|
||||||
|
buffer[len] = '\0';
|
||||||
|
fclose(fp);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
return default_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 通用配置保存函数 */
|
||||||
|
static int save_config_value(const char *key, const char *value) {
|
||||||
|
mkdir(CONFIG_DIR, 0700);
|
||||||
|
|
||||||
|
FILE *fp = fopen(CONFIG_FILE, "r");
|
||||||
|
char temp_file[MAX_PATH_LEN];
|
||||||
|
snprintf(temp_file, sizeof(temp_file), "%s.tmp", CONFIG_FILE);
|
||||||
|
FILE *temp_fp = fopen(temp_file, "w");
|
||||||
|
|
||||||
|
if (!temp_fp) {
|
||||||
|
if (fp) fclose(fp);
|
||||||
|
return ERROR_FILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
size_t key_len = strlen(key);
|
||||||
|
|
||||||
|
if (fp) {
|
||||||
|
char line[MAX_LINE_LEN];
|
||||||
|
while (fgets(line, sizeof(line), fp)) {
|
||||||
|
if (strncmp(line, key, key_len) == 0 && line[key_len] == '=') {
|
||||||
|
fprintf(temp_fp, "%s=%s\n", key, value);
|
||||||
|
found = true;
|
||||||
|
} else {
|
||||||
|
fputs(line, temp_fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
fprintf(temp_fp, "%s=%s\n", key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(temp_fp);
|
||||||
|
chmod(temp_file, 0600);
|
||||||
|
rename(temp_file, CONFIG_FILE);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_rate_limit_from_config(void) {
|
||||||
|
return get_config_int("RATE_LIMIT", DEFAULT_RATE_LIMIT, 1, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
int save_rate_limit_to_config(int rate_limit) {
|
||||||
|
if (rate_limit <= 0 || rate_limit > 1000) {
|
||||||
|
return ERROR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
char buf[32];
|
||||||
|
snprintf(buf, sizeof(buf), "%d", rate_limit);
|
||||||
|
return save_config_value("RATE_LIMIT", buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* get_rate_ban_time_from_config(void) {
|
||||||
|
static char ban_time[32] = {0};
|
||||||
|
return get_config_str("RATE_BAN_TIME", DEFAULT_RATE_BAN_TIME, ban_time, sizeof(ban_time));
|
||||||
|
}
|
||||||
|
|
||||||
|
int save_rate_ban_time_to_config(const char *ban_time) {
|
||||||
|
if (!ban_time) {
|
||||||
|
return ERROR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
return save_config_value("RATE_BAN_TIME", ban_time);
|
||||||
|
}
|
||||||
|
|||||||
@@ -155,6 +155,9 @@ int install_service(void) {
|
|||||||
/* 创建默认配置文件 */
|
/* 创建默认配置文件 */
|
||||||
if (access(CONFIG_FILE, F_OK) != 0) {
|
if (access(CONFIG_FILE, F_OK) != 0) {
|
||||||
save_ban_time_to_config(DEFAULT_BAN_TIME);
|
save_ban_time_to_config(DEFAULT_BAN_TIME);
|
||||||
|
save_max_retries_to_config(DEFAULT_MAX_RETRIES);
|
||||||
|
save_rate_limit_to_config(DEFAULT_RATE_LIMIT);
|
||||||
|
save_rate_ban_time_to_config(DEFAULT_RATE_BAN_TIME);
|
||||||
msg(C_GREEN, " ✓ 已创建默认配置文件");
|
msg(C_GREEN, " ✓ 已创建默认配置文件");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+47
-4
@@ -21,10 +21,12 @@ void show_help(void) {
|
|||||||
printf(" bip vip add <IP> 添加IP到白名单 (支持IPv4/IPv6/CIDR)\n");
|
printf(" bip vip add <IP> 添加IP到白名单 (支持IPv4/IPv6/CIDR)\n");
|
||||||
printf(" bip vip del <IP> 从白名单移除IP\n");
|
printf(" bip vip del <IP> 从白名单移除IP\n");
|
||||||
printf(" bip vip list 显示白名单列表\n");
|
printf(" bip vip list 显示白名单列表\n");
|
||||||
printf(" bip config 显示当前配置\n");
|
printf(" bip config 显示当前配置\n");
|
||||||
printf(" bip config time <time> 设置封禁时间 (如: 7d, 24h, \"\" 为永久)\n");
|
printf(" bip config time <time> 设置封禁时间 (如: 7d, 24h, \"\" 为永久)\n");
|
||||||
printf(" bip config retries <N> 设置最大重试次数 (1-10)\n");
|
printf(" bip config retries <N> 设置最大重试次数 (1-10)\n");
|
||||||
printf(" bip restore 从持久化文件恢复黑白名单\n");
|
printf(" bip config ratelimit <N> 设置SSH端口速率 (1-1000/分钟)\n");
|
||||||
|
printf(" bip config rateban <time> 设置超速封禁时长 (如: 10m, 1h)\n");
|
||||||
|
printf(" bip restore 从持久化文件恢复黑白名单\n");
|
||||||
printf(" bip install 安装/重装服务\n");
|
printf(" bip install 安装/重装服务\n");
|
||||||
printf(" bip uninstall 卸载服务\n");
|
printf(" bip uninstall 卸载服务\n");
|
||||||
printf("--------------------------------------------------------\n");
|
printf("--------------------------------------------------------\n");
|
||||||
@@ -138,7 +140,10 @@ int main(int argc, char *argv[]) {
|
|||||||
/* 显示当前配置 */
|
/* 显示当前配置 */
|
||||||
const char *ban_time = get_ban_time_from_config();
|
const char *ban_time = get_ban_time_from_config();
|
||||||
int max_retries = get_max_retries_from_config();
|
int max_retries = get_max_retries_from_config();
|
||||||
|
int rate_limit = get_rate_limit_from_config();
|
||||||
|
const char *rate_ban_time = get_rate_ban_time_from_config();
|
||||||
printf("%s当前配置%s\n", C_CYAN, C_RESET);
|
printf("%s当前配置%s\n", C_CYAN, C_RESET);
|
||||||
|
printf("====防爆破===\n");
|
||||||
printf("封禁时间: %s%s%s", C_GREEN, ban_time, C_RESET);
|
printf("封禁时间: %s%s%s", C_GREEN, ban_time, C_RESET);
|
||||||
if (strlen(ban_time) == 0) {
|
if (strlen(ban_time) == 0) {
|
||||||
printf(" (永久封禁)\n");
|
printf(" (永久封禁)\n");
|
||||||
@@ -146,6 +151,9 @@ int main(int argc, char *argv[]) {
|
|||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
printf("最大重试次数: %s%d%s\n", C_GREEN, max_retries, C_RESET);
|
printf("最大重试次数: %s%d%s\n", C_GREEN, max_retries, C_RESET);
|
||||||
|
printf("====防洪水攻击===\n");
|
||||||
|
printf("SSH端口速率: %s%d/分钟%s\n", C_GREEN, rate_limit, C_RESET);
|
||||||
|
printf("超速封禁时长: %s%s%s\n", C_GREEN, rate_ban_time, C_RESET);
|
||||||
printf("配置文件: %s\n", CONFIG_FILE);
|
printf("配置文件: %s\n", CONFIG_FILE);
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
} else if (argc == 4 && strcmp(argv[2], "time") == 0) {
|
} else if (argc == 4 && strcmp(argv[2], "time") == 0) {
|
||||||
@@ -176,10 +184,45 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
msg(C_RED, "❌ 设置失败: 请使用1-10之间的整数");
|
msg(C_RED, "❌ 设置失败: 请使用1-10之间的整数");
|
||||||
return ERROR_INVALID_ARG;
|
return ERROR_INVALID_ARG;
|
||||||
|
} else if (argc == 4 && strcmp(argv[2], "ratelimit") == 0) {
|
||||||
|
/* 设置SSH端口速率 */
|
||||||
|
int rate = atoi(argv[3]);
|
||||||
|
if (save_rate_limit_to_config(rate) == SUCCESS) {
|
||||||
|
char msg_buf[MAX_LINE_LEN];
|
||||||
|
snprintf(msg_buf, sizeof(msg_buf), "✅ SSH端口速率已设置为: %d/分钟", rate);
|
||||||
|
msg(C_GREEN, msg_buf);
|
||||||
|
/* 自动重新加载nftables规则 */
|
||||||
|
if (init_nftables_rules() == SUCCESS) {
|
||||||
|
msg(C_GREEN, "✅ 已自动应用新的速率限制规则");
|
||||||
|
} else {
|
||||||
|
msg(C_YELLOW, "⚠️ 规则应用失败,请手动运行: sudo bip install");
|
||||||
|
}
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
msg(C_RED, "❌ 设置失败: 请使用1-1000之间的整数");
|
||||||
|
return ERROR_INVALID_ARG;
|
||||||
|
} else if (argc == 4 && strcmp(argv[2], "rateban") == 0) {
|
||||||
|
/* 设置超速封禁时间 */
|
||||||
|
const char *new_time = argv[3];
|
||||||
|
if (save_rate_ban_time_to_config(new_time) == SUCCESS) {
|
||||||
|
char msg_buf[MAX_LINE_LEN];
|
||||||
|
snprintf(msg_buf, sizeof(msg_buf), "✅ 超速封禁时长已设置为: %s", new_time);
|
||||||
|
msg(C_GREEN, msg_buf);
|
||||||
|
/* 自动重新加载nftables规则 */
|
||||||
|
if (init_nftables_rules() == SUCCESS) {
|
||||||
|
msg(C_GREEN, "✅ 已自动应用新的封禁时长规则");
|
||||||
|
} else {
|
||||||
|
msg(C_YELLOW, "⚠️ 规则应用失败,请手动运行: sudo bip install");
|
||||||
|
}
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
return ERROR_FILE;
|
||||||
} else {
|
} else {
|
||||||
msg(C_RED, "用法: bip config");
|
msg(C_RED, "用法: bip config");
|
||||||
msg(C_RED, " bip config time <time>");
|
msg(C_RED, " bip config time <time>");
|
||||||
msg(C_RED, " bip config retries <count>");
|
msg(C_RED, " bip config retries <count>");
|
||||||
|
msg(C_RED, " bip config ratelimit <rate>");
|
||||||
|
msg(C_RED, " bip config rateban <time>");
|
||||||
return ERROR_INVALID_ARG;
|
return ERROR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-9
@@ -95,15 +95,6 @@ int init_nftables_rules(void) {
|
|||||||
NFT_TABLE, NFT_WHITELIST, NFT_TABLE, NFT_WHITELIST);
|
NFT_TABLE, NFT_WHITELIST, NFT_TABLE, NFT_WHITELIST);
|
||||||
system(command);
|
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 */
|
/* 2. IPv6白名单 accept */
|
||||||
snprintf(command, sizeof(command),
|
snprintf(command, sizeof(command),
|
||||||
"nft list chain %s input | grep -q '@%s' || nft add rule %s input ip6 saddr @%s accept",
|
"nft list chain %s input | grep -q '@%s' || nft add rule %s input ip6 saddr @%s accept",
|
||||||
@@ -121,7 +112,41 @@ int init_nftables_rules(void) {
|
|||||||
"nft list chain %s input | grep -q '@%s' || nft add rule %s input ip6 saddr @%s drop",
|
"nft list chain %s input | grep -q '@%s' || nft add rule %s input ip6 saddr @%s drop",
|
||||||
NFT_TABLE, NFT_SET_V6, NFT_TABLE, NFT_SET_V6);
|
NFT_TABLE, NFT_SET_V6, NFT_TABLE, NFT_SET_V6);
|
||||||
system(command);
|
system(command);
|
||||||
|
|
||||||
|
/* 5. SSH端口速率(防止TCP洪水,超速临时封禁) */
|
||||||
|
int ssh_port = get_ssh_port();
|
||||||
|
int rate_limit = get_rate_limit_from_config();
|
||||||
|
const char *rate_ban_time = get_rate_ban_time_from_config();
|
||||||
|
|
||||||
|
/* 创建SSH端口速率动态集合 */
|
||||||
|
snprintf(command, sizeof(command),
|
||||||
|
"nft add set %s ssh-ratelimit '{ type ipv4_addr; size 65535; flags dynamic,timeout; }' 2>/dev/null",
|
||||||
|
NFT_TABLE);
|
||||||
|
system(command);
|
||||||
|
snprintf(command, sizeof(command),
|
||||||
|
"nft add set %s ssh-ratelimit_v6 '{ type ipv6_addr; size 65535; flags dynamic,timeout; }' 2>/dev/null",
|
||||||
|
NFT_TABLE);
|
||||||
|
system(command);
|
||||||
|
|
||||||
|
/* 删除旧的限速规则(如果存在) */
|
||||||
|
snprintf(command, sizeof(command),
|
||||||
|
"nft -a list chain %s input 2>/dev/null | grep -E 'ssh-ratelimit.*tcp dport' | awk '{print $NF}' | "
|
||||||
|
"xargs -r -I {} nft delete rule %s input handle {}",
|
||||||
|
NFT_TABLE, NFT_TABLE);
|
||||||
|
system(command);
|
||||||
|
|
||||||
|
/* 添加新的限速规则:超速IP加入临时封禁集合 */
|
||||||
|
snprintf(command, sizeof(command),
|
||||||
|
"nft add rule %s input tcp dport %d ct state new "
|
||||||
|
"add @ssh-ratelimit { ip saddr timeout %s limit rate over %d/minute burst 5 packets } drop",
|
||||||
|
NFT_TABLE, ssh_port, rate_ban_time, rate_limit);
|
||||||
|
system(command);
|
||||||
|
snprintf(command, sizeof(command),
|
||||||
|
"nft add rule %s input tcp dport %d ct state new "
|
||||||
|
"add @ssh-ratelimit_v6 { ip6 saddr timeout %s limit rate over %d/minute burst 5 packets } drop",
|
||||||
|
NFT_TABLE, ssh_port, rate_ban_time, rate_limit);
|
||||||
|
system(command);
|
||||||
|
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user