#include #include #include #include #include #include #include #include #include #define CENTOS_SYSTEM 1 #define DEBISN_SYSTEM 2 #define UNKNOWN_SYSTEM 3 #define BUFFER 1024 #define LONG_BUFFER 1024*100 #define TOP_IP 10 #define AWK " | awk -v num=%d '{a[$1]+=1;} END {for(i in a){if (a[i] >= num) {print i;}}}' " #define GE_10 "grep -E \"^$(LC_ALL=\"C\" date \"+%h\").$(LC_ALL=\"C\" date \"+%d\")\" /var/log/auth.log | grep failure | grep rhost" #define LE_10 "grep -E \"^$(LC_ALL=\"C\" date \"+%h\")..$(LC_ALL=\"C\" date | awk '{print $3}')\" /var/log/auth.log | grep failure | grep rhost" #define CENTOS_GE_10 "grep -E \"^$(LC_ALL=\"C\" date \"+%h\").$(LC_ALL=\"C\" date \"+%d\")\" /var/log/secure | grep failure | grep rhost" #define CENTOS_LE_10 "grep -E \"^$(LC_ALL=\"C\" date \"+%h\")..$(LC_ALL=\"C\" date | awk '{print $3}')\" /var/log/secure | grep failure | grep rhost" #define IPTABLES "iptables -I INPUT -s %s -j DROP" #define IPTABLES_CHECK "iptables -C INPUT -s %s -j DROP" #define TIME 60 #define IS_DIND 1 #define PHONE "155659790" #define DING_CURL "https://oapi.dingtalk.com/robot/send?access_token=7f069c672cb878987aa6772cca336740eece4ce36bde12b51b45e9f440e" #define IS_MAIL 0 #define IS_QQMAIL 0 #define QQMAIL "qqMail -l smtp.qq.com -p 25 -f 1605227279 -e caczsjchvyibi -q NIUYULING -r 1605227279@QQ.COM -n NIUYULING -s \"System ban IP\" -t \"%s\"" // 存储公网IP char public_ip[BUFFER]; // 检测系统 int check_system() { if (0 == access("/etc/debian_version", F_OK)) { return DEBISN_SYSTEM; } else if (0 == access("/etc/centos-release", F_OK)) { return CENTOS_SYSTEM; } return UNKNOWN_SYSTEM; } // 获取公网IP char *get_public_ip(char *ip) { FILE *fp; char buff[BUFFER]; memset(buff, 0, BUFFER); if (NULL == (fp = popen("curl members.3322.org/dyndns/getip --silent", "r"))) { perror("popen curl"); } while (fgets(buff, BUFFER, fp) != NULL) { buff[strlen(buff) - 1] = '\0'; } if (NULL != fp) pclose(fp); return strcpy(ip, buff); } // 替换字符串 int strReplaceAll(char *str, char *sub, char *replace) { if (NULL == str || NULL == sub || NULL == replace) { printf("strReplaceAll param error\n"); return 1; } char *p = NULL; char *t = NULL; char *q = NULL; char *dst = NULL; char *src = NULL; int str_len = strlen(str); int sub_len = strlen(sub); int replace_len = strlen(replace); p = str; while ('\0' != *p) { t = str + str_len; q = strstr(str, sub); if (NULL == q) /* 没有子串了,那么直接返回吧 */ break; src = q + sub_len; /* 源头, 原有sub后的一个字符 */ dst = q + replace_len; /* 目的,放完replace后的一个字符 */ memcpy(dst, src, t - src); /* 原有字符串后移,放出空间 */ memcpy(q, replace, replace_len); /* 将replace字符拷贝进来 */ str_len = str_len + replace_len - sub_len; p = q + replace_len; /* p 下一轮replace后的一个字符 */ } str[str_len] = '\0'; /* 通过'\0'表示结尾 */ return 0; } // 钉钉告警 int dingding_warning(char *illegal_ip, char *public_ip) { CURL *curl; CURLcode res; curl_global_init(CURL_GLOBAL_ALL); curl = curl_easy_init(); if (curl == NULL) { return 1; } char jsonObj[BUFFER] = "{ \ \"msgtype\": \"text\", \ \"text\": { \ \"content\": \"Alert @PHONE 主机PUBLIC 非法主机IP被封禁! Warning!\" \ }, \ \"at\": { \ \"atMobiles\": [\"PHONE\"], \ \"isAtAll\": false \ } \ }"; strReplaceAll(jsonObj, "IP", illegal_ip); strReplaceAll(jsonObj, "PHONE", PHONE); strReplaceAll(jsonObj, "PUBLIC", public_ip); printf("%s\n", jsonObj); struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "Accept: application/json"); headers = curl_slist_append(headers, "Content-Type: application/json"); headers = curl_slist_append(headers, "charset: utf-8"); curl_easy_setopt(curl, CURLOPT_URL, DING_CURL); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); curl_easy_setopt(curl, CURLOPT_POST, 1); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonObj); curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl/0.1"); res = curl_easy_perform(curl); curl_easy_cleanup(curl); curl_global_cleanup(); return res; } // 邮件告警 int mail_warning(char *illegal_ip, char *public_ip) { FILE *fp = NULL; char buff[BUFFER]; char text[BUFFER] = "echo \"主机:HOST, 禁止IP访问!\" |"; memset(buff, 0, BUFFER); strReplaceAll(text, "IP", illegal_ip); strReplaceAll(text, "HOST", public_ip); strcat(text, " mail -s \"System ban IP\" 1605227279@qq.com"); if (NULL == (fp = popen(text, "r"))) { perror("popen text"); } while (fgets(buff, BUFFER, fp) != NULL) { buff[strlen(buff) - 1] = '\0'; } if (NULL != fp) pclose(fp); return 0; } // 第三方邮箱告警 int QQ_mail_warning(char *illegal_ip, char *public_ip) { char string[BUFFER+(sizeof(QQMAIL))]; char text[BUFFER] = "主机:HOST, 禁止IP访问!"; strReplaceAll(text, "IP", illegal_ip); strReplaceAll(text, "HOST", public_ip); memset(string, 0, BUFFER+(sizeof(QQMAIL))); sprintf(string, QQMAIL, text); system(string); return 0; } // 封禁非法IP int rule() { FILE *fp, *fc; char p[2], splice_command[LONG_BUFFER], command[LONG_BUFFER], *temp, buffer[BUFFER], awk[BUFFER], iptables[BUFFER + (sizeof(IPTABLES))], iptables_check[BUFFER + (sizeof(IPTABLES))]; time_t timep; struct tm *tp; time(&timep); tp = localtime(&timep); memset(splice_command, 0, LONG_BUFFER); memset(command, 0, LONG_BUFFER); memset(buffer, 0, BUFFER); memset(awk, 0, BUFFER); memset(iptables, 0, BUFFER+(sizeof(IPTABLES))); memset(iptables_check, 0, BUFFER+(sizeof(IPTABLES))); fp = NULL; fc = NULL; if (DEBISN_SYSTEM == check_system()) // Debian 系统规则 { if (tp->tm_mday >= 10) { if ((fp = popen(GE_10, "r")) == NULL) { perror("GE_10"); return 1; } } else { if ((fp = popen(LE_10, "r")) == NULL) { perror("LE_10"); return 1; } } } else if (CENTOS_SYSTEM == check_system()) // Centos 7系统规则 { if (tp->tm_mday >= 10) { if ((fp = popen(CENTOS_GE_10, "r")) == NULL) { perror("CENTOS_GE_10"); return 1; } } else { if ((fp = popen(CENTOS_LE_10, "r")) == NULL) { perror("CENTOS_LE_10"); return 1; } } } else { return UNKNOWN_SYSTEM; } while (fgets(buffer, BUFFER, fp) != NULL) { temp = strstr(buffer, "rhost"); sscanf(temp, "rhost=%s", temp); if (atoi(strncpy(p, temp, 1)) > 0) { strcat(splice_command, temp); strcat(splice_command, "\n"); } } printf("%s", splice_command); // 打印所有非法IP // 拼接命令 sprintf(awk, AWK, TOP_IP); strcpy(command, "echo \""); strcat(command, splice_command); strcat(command, "\""); strcat(command, awk); if ((fp = popen(command, "r")) == NULL) // 执行命令 { perror("popen command"); return 1; } while (fgets(buffer, BUFFER, fp) != NULL) // 执行命令后, 为空时就不会 { buffer[strlen(buffer) - 1] = '\0'; // 去除回车 sprintf(iptables, IPTABLES, buffer); sprintf(iptables_check, IPTABLES_CHECK, buffer); if (0 != system(iptables_check)) // 判断是否存在规则, 不存在时再添加规则 { if (IS_DIND == 1) // 钉钉告警 { dingding_warning(buffer, public_ip); sleep(3); } if (IS_MAIL == 1) // 邮件告警 { mail_warning(buffer, public_ip); sleep(3); } if (IS_QQMAIL == 1) // 邮件告警 { QQ_mail_warning(buffer, public_ip); sleep(3); } if ((fc = popen(iptables, "r")) == NULL) { perror("popen iptables"); return 1; } } } if (fp != NULL) pclose(fp); if (fc != NULL) pclose(fc); return 0; } static void sig_child(int signo) { pid_t pid; int stat; // 处理僵尸进程 while ((pid = waitpid(-1, &stat, WNOHANG)) > 0) ; return; } int main(int argc, char *argv[], char **env) { memset(public_ip, 0, BUFFER); get_public_ip(public_ip); signal(SIGCHLD, sig_child); // 创建捕捉子进程退出信号 if (argv[1] != NULL && 0 == strcmp(argv[1], "-d")) { if (daemon(1, 1)) // 守护进程 { perror("daemon"); return -1; } while (1) { rule(); sleep(TIME); } } else { rule(); } return 0; }