diff --git a/Makefile b/Makefile index 4f1f793..8736236 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,11 @@ CROSS_COMPILE ?= CC := $(CROSS_COMPILE)gcc -CFLAGS += -O2 -g -Wall - +CFLAGS += -g -Wall +LIB += -lcurl OBG = rhost all: rhost.o - $(CC) $(CFLAGS) $^ -o $(OBG) + $(CC) $(CFLAGS) $^ -o $(OBG) $(LIB) clean: rm -rf *.o diff --git a/README.md b/README.md index 60d4639..f6a0071 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,11 @@ # denyhosts ssh防止暴力破解,适用Debian 8、9 +支持钉钉告警和邮件告警 +支持第三方QQ邮箱告警 +支持一次运行检测、后台运行检测 +请安装libcurl库 -## Help Information cd /root git clone https://github.com/niuyuling/denyhosts.git cd denyhosts @@ -12,3 +15,5 @@ chmod a+x /root/denyhosts/denyhosts.sh crontab 定时任务,像这样. 0 22 * * * /root/denyhosts/denyhosts.sh + + diff --git a/denyhosts.sh b/denyhosts.sh index 35b66a2..fdf6db2 100644 --- a/denyhosts.sh +++ b/denyhosts.sh @@ -42,6 +42,7 @@ function run() mail -s "System Log" ${email_address} < ${log_file} rm ${log_file} fi + sync } diff --git a/rhost b/rhost index 0d124a5..90f79e1 100644 Binary files a/rhost and b/rhost differ diff --git a/rhost.c b/rhost.c index e845422..27fcbab 100644 --- a/rhost.c +++ b/rhost.c @@ -4,19 +4,212 @@ #include #include #include +#include +#include +#include -#define BUFFER 270 +#define CENTOS_SYSTEM 1 +#define DEBISN_SYSTEM 2 +#define UNKNOWN_SYSTEM 3 + +#define BUFFER 1024 #define LONG_BUFFER 1024*100 -#define TOP_IP 20 -#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 \"^$(date \"+%h\").$(date \"+%d\")\" /var/log/auth.log | grep failure | grep rhost" -#define LE_10 "grep -E \"^$(date \"+%h\")..$(date | awk '{print $3}')\" /var/log/auth.log | grep failure | grep rhost" -#define IPTABLES "/sbin/iptables -I INPUT -s %s -j DROP" +#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" -int main(int argc, char *argv[], char **env) +#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]; + 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; @@ -26,20 +219,42 @@ int main(int argc, char *argv[], char **env) memset(command, 0, LONG_BUFFER); memset(buffer, 0, BUFFER); memset(awk, 0, BUFFER); - memset(iptables, 0, BUFFER); + memset(iptables, 0, BUFFER+(sizeof(IPTABLES))); + memset(iptables_check, 0, BUFFER+(sizeof(IPTABLES))); fp = NULL; fc = NULL; - - if (tp->tm_mday >= 10) { - if ((fp = popen(GE_10, "r")) == NULL) { - return 1; + + 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 { - if ((fp = popen(LE_10, "r")) == NULL) { - return 1; - } + return UNKNOWN_SYSTEM; } - + while (fgets(buffer, BUFFER, fp) != NULL) { temp = strstr(buffer, "rhost"); sscanf(temp, "rhost=%s", temp); @@ -48,32 +263,107 @@ int main(int argc, char *argv[], char **env) strcat(splice_command, "\n"); } } - printf("%s", splice_command); // 测试没问题 - + printf("%s", splice_command); // 打印所有非法IP + // 拼接命令 sprintf(awk, AWK, TOP_IP); strcpy(command, "echo \""); strcat(command, splice_command); strcat(command, "\""); strcat(command, awk); - //printf("%s", command); // 测试没问题 - if ((fp = popen(command, "r")) == NULL) { // 执行命令 - perror("popen"); + + + if ((fp = popen(command, "r")) == NULL) // 执行命令 + { + perror("popen command"); return 1; } - while (fgets(buffer, BUFFER, fp) != NULL) { - buffer[strlen(buffer) - 1] = '\0'; // 去除回车 + + while (fgets(buffer, BUFFER, fp) != NULL) // 执行命令后, 为空时就不会 + { + + buffer[strlen(buffer) - 1] = '\0'; // 去除回车 sprintf(iptables, IPTABLES, buffer); - if ((fc = popen(iptables, "r")) == NULL) { - perror("popen"); - return 1; + 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; + } } } - pclose(fp); - pclose(fc); - - + 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; +} + diff --git a/rhost.o b/rhost.o index 652b2dc..ab1531c 100644 Binary files a/rhost.o and b/rhost.o differ