赞
踩
最近发现公司线上的应用老出现访问异常的情况,排查nginx的access.log日志文件,发现了大量恶意攻击的代码,内容如下
主要内容为"\x03\x00\x00/*\xE0\x00\x00\x00\x00\x00Cookie: mstshash=Administr" 400 173 “-” “-”
一般黑客的ip地址查询都是在国外的
然后想起用nginx的ip黑名单来限制
在nginx.conf配置文件中http标签中加入
#把94.232.40.111列入黑名单
deny 94.232.40.111;
由于在nginx.conf中直接写入deny代码不怎么优雅,把信息提取成一个配置文件中
在nginx.conf文件的路径同级写入一个blackListIp.conf文件
blackListIp.conf文件内容如下
deny 94.232.40.111;
在nginx中的http标签中引入黑名单配置文件
http {
#屏蔽黑名单IP
include blackListIp.conf;
}
运行一段时间发现还是会出现nginx被恶意攻击宕机了,后面查看nginx的access.log日志发现了大量模式的黑客IP,再排查error.log内容如下
关键信息 access forbidden by rule 是拒绝访问意思,表示黑名单是生效了
但是手动去access.log日志找黑客IP然后加入黑名单配置文件太麻烦,于是手撸了一个java工具类解决此问题.
可以根据需求自行扩展
/**
* 自定义匹配恶意攻击的信息
*/
private static final List<String> MATCHER_LIST = Arrays.asList("mstshash=Administr");
/**
* 解析nginx日志中的非法访问的IP地址
* @param logPath
* @return 非法IP地址
* @throws Exception
*/
private static Set<String> readNginxAccess(String logPath) throws Exception {
File file = new File(logPath);
if (!file.exists()) {
logger.error("{} not exists", logPath);
return null;
}
Set<String> set = new HashSet<>();
InputStreamReader ir = new InputStreamReader(new FileInputStream(file));
LineNumberReader input = new LineNumberReader(ir);
String line;
while ((line = input.readLine()) != null) {
final String fLine = line;
for (String str : MATCHER_LIST) {
//如果匹配上了,把ip加入set集合并结束for循环
if (fLine.contains(str)) {
System.out.println(fLine);
set.add(fLine.split(" ")[0]);
break;
}
}
}
return set;
}
/**
* 换行符
*/
private static String lineSeparator = "\r\n";
/**
* 日期格式
*/
private final static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy_MM_dd_HH_ss");
/**
* 写入黑名单列表
* @param blackListConfigPath 黑名单配置文件路径
* @param blackListSet 扫描出来的黑客ip集合
* @return (如果扫描出来的黑客IP地址和已存在的不完全重叠, 返回true, 否则false)
* @throws Exception
*/
private static boolean writeBlacklist(String blackListConfigPath, Set<String> blackListSet) throws Exception {
File file = new File(blackListConfigPath);
if (!file.exists()) {
file.createNewFile();
}
List<String> list = new ArrayList<>();
InputStreamReader ir = new InputStreamReader(new FileInputStream(file));
LineNumberReader input = new LineNumberReader(ir);
String line;
String ip;
while ((line = input.readLine()) != null) {
if ("".equals(line.trim()) || line.startsWith("#")) {
continue;
}
ip = line.split(" ")[1];
ip = ip.substring(0, ip.length() - 1);
list.add(ip);
}
//遍历添加黑名单列表中不存在的IP
Set<String> filterSet = new HashSet();
blackListSet.forEach(ipaddr -> {
if (!list.contains(ipaddr)) {
filterSet.add(ipaddr);
}
});
if (filterSet.size() > 0) {
FileWriter out = new FileWriter(file, true);
BufferedWriter bw = new BufferedWriter(out);
for (String ipaddr : filterSet) {
bw.write(lineSeparator);
bw.write(String.format("deny %s;", ipaddr));
logger.info("黑客IP:{} 已写入nginx黑名单列表", ipaddr);
}
//写入文件修改时间
bw.write(lineSeparator);
bw.write("#" + simpleDateFormat.format(new Date()));
bw.flush();
bw.close();
return true;
} else {
logger.info("未发现新的黑客IP地址");
return false;
}
}
/**
* 扫描黑名单
* @param logPath access.log文件的路径
* @param blackListPath 黑名单配置文件路径
* @param binPath nginx的启动文件路径
* @throws Exception
*/
public static void scanningBlackList(String logPath, String blackListPath, String binPath) throws Exception {
//第一步 找到非法访问IP
Set<String> set = NginxUtil.readNginxAccess(logPath);
if (set!=null && set.size() > 0) {
//第二步,写入非法IP进nginx的blackList文件
boolean flag = NginxUtil.writeBlacklist(blackListPath, set);
//第三步,备份之前的access.log,刷新nginx配置使黑名单生效
if (flag) {
//备份nginx日志文件
String backUpLogPath = logPath + "_backup_" + simpleDateFormat.format(new Date());
Process process = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", String.format("mv %s %s", logPath, backUpLogPath)}, null, null);
process.waitFor();
//执行 /usr/local/nginx/nginx -s reload 会重新生成一个新的access.log文件
process = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", String.format("%s -s reload", binPath)}, null, null);
process.waitFor();
}
}
}
下面通过使用java源生的定时任务线程池**(scheduledThreadPool** )来执行任务,如果不需要集成了web应用中,则下面的代码打包完放在服务器上面运行就可以了,如果需要集成到web应用中,则请查看下面的步骤6
public static void main(String[] args) throws Exception {
//nginx的工作空间
String nginxPath = "/usr/local/nginx/";
//nging的access.log路径
String logPath = nginxPath + "logs/access.log";
//黑名单配置文件路径
String blackListPath = nginxPath + "conf/blackListIp.conf";
//启动脚本文件路径
String binPath = nginxPath + "bin/nginx";
//使用单线程线程池执行定时任务
ScheduledExecutorService scheduledThreadPool = Executors.newSingleThreadScheduledExecutor();
//应用启动10秒后,每隔30秒执行一次任务
scheduledThreadPool.scheduleAtFixedRate(()->{
try {
scanningBlackList(logPath, blackListPath, binPath);
} catch (Exception e) {
e.printStackTrace();
}
}, 10, 30, TimeUnit.SECONDS);
}
application.yml
nginx:
#恶意攻击的内容,多组用 , 隔离
matcher_list: mstshash=Administr,\x16\x03\x01\x00{
#工作空间
workspace: /usr/local/nginx/
#启动脚本
sbin: ${nginx.workspace}sbin/nginx
#日志文件
log: ${nginx.workspace}logs/access.log
#黑名单IP存储文件
blackListIp: ${nginx.workspace}conf/blackListIp.conf
@Configuration
@ConfigurationProperties(prefix = "nginx")
public class NginxParam {
private String sbin;
private String log;
private String blackListIp;
private List<String> matcherList;
//省略get set
}
@Component
public class NginxScheduling {
@Autowired
NginxParam nginxParam;
/**
* bean加载的时候执行的初始化操作
*/
@PostConstruct
public void init() {
NginxUtil.setMatcherList(nginxParam.getMatcherList());
}
/**
* 扫描非法IP
* 每隔30秒扫描一次非法IP并加入黑名单
*/
@Scheduled(fixedDelay = 30000)
public void ScanForIllegalIP() throws Exception {
NginxUtil.scanningBlackList(nginxParam.getLog(),nginxParam.getBlackListIp(),nginxParam.getSbin());
}
}
基于之前定义的NginxUtil工具类,新增一个setMatcherList方法,把配置文件定义的恶意攻击的信息注入到工具类中
public class NginxUtil {
/**
* 自定义匹配恶意攻击的信息
*/
private static List<String> MATCHER_LIST;
public static void setMatcherList(List<String> matcherList) {
MATCHER_LIST = matcherList;
}
}
@SpringBootApplication
@EnableScheduling
public class GuardApplication {
public static void main(String[] args) {
SpringApplication.run(GuardApplication.class, args);
}
}
如果没这个需要就忽略下面代码。
配置文件新增属性
nginx:
#nginx部署的某个服务端口
pingPort: http://127.0.0.1:38888
在NginxParam配置类新增pingPort字段
public class NginxParam {
private String pingPort;
}
在NginxUtil工具类中添加以下代码
public class NginxUtil {
/**
* 监测nginx进程是否存活
*/
public static void monitor(String bin, String pingPort) {
try {
//执行 curl访问nginx启动的某个端口,如果不存在则表示nginx已经挂了
Process process = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", "curl " + pingPort}, null, null);
InputStreamReader isr = new InputStreamReader(process.getErrorStream());
BufferedReader br = new BufferedReader(isr);
String line;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line);
}
process.waitFor();
String res = sb.toString();
if (StringUtils.hasLength(res) && res.contains("Connection refused")) {
logger.info("nginx process abnormal,restart ....");
process = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", bin}, null, null);
process.waitFor();
} else {
logger.info("nginx process normal");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
在定时任务类中添加新任务,一分钟检测一次nginx心跳,如果nginx宕机了就自动重启
@Component
public class NginxScheduling {
/**
* 健康监测
* @throws Exception
*/
@Scheduled(fixedDelay = 60000)
public void healthMonitoring() {
NginxUtil.monitor(nginxParam.getSbin(),nginxParam.getPingPort());
}
}
查看nginx的log目录,发现备份文件多了几个,代表着已经有新的黑客IP被列入黑名单了
查看/usr/local/nginx/conf/blackListIp.conf文件发现已经新增了几个IP了,代表着任务已经完成了.
gitee地址
配套代码里在博客里的功能基础上加入了监控nginx进程宕机了重启的功能和把国外的IP地址访问拉入黑名单功能。
要是觉得我写的对你有点帮助的话,麻烦在gitee上帮我点 Star
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。