配置 logstash 监控 nginx access log 写入 mysql

2018-10-27 Mithrilwoodrat 更多博文 » 博客 » GitHub »

原文链接 http://woodrat.xyz/2018/10/27/%E9%85%8D%E7%BD%AE%20logstash%20%E7%9B%91%E6%8E%A7%20nginx%20access%20log%20%E5%86%99%E5%85%A5%20mysql/
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


在存储能力和数据量有限的情况下,我们可以选择 MySQL 来进行简单的数据存储和分析。

Logstash 支持监听文件更新,并可以解析日志后写入 MySQL 数据库。使用 Logstash 可以免去繁琐的开发流程,快速实现旁路日志监控上报功能。

要使用 Logstash 监控 Nginx access log 转储到 MySQL 中,需要用到 Logstash 的3个插件。即 file input plugin,grok filter plugin 以及 jdbc output plugin。

本文基于 Logstash 5.6 版本,其余版本只是配置语法和安装方法会有细微不同,使用方法均类似。示例的运行环境为 Ubuntu 16.04

~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 16.04.1 LTS
Release: 16.04
Codename: xenial

一.安装 Logstash 及配置所需插件

首先我们需要从官网上下载对应版本的 logstash 安装包。

wget https://artifacts.elastic.co/downloads/logstash/logstash-5.6.0.tar.gz

logstash 5.6 版本需要机器上安装有 openjdk 8,若安装了 openjdk 9 则需要手动降级或设置环境变量。

安装好 jdk 后,解压缩 logstash-5.6.0 版本的 tgz 包即可运行。

$ tar xvf logstash-5.6.0.tar.gz
$ ./logstash-5.6.4/bin/logstash --version
logstash 5.6.4

不过, logstash 是通过插件系统来支持的将结果写入到数据库,默认的安装包中没有自带 logstash-output-jdbc 插件,需要手动安装。

1.安装 logstash-output-jdbc

logstash-output-jdbc 的安装分为两步,第一步是安装插件本身,插件安装成功后还需要下载 jdbc driver 的 jar 包并配置。

通常情况下,调用 logstash 自带的 logstash-plugin 工具即可成功安装该插件。

../logstash-5.6.4/bin/logstash-plugin install logstash-output-jdbc

但是在网络环境不够良好的情况下, Logstash 也支持插件的离线安装。

先在一台已经成功安装了 logstash-output-jdbc 插件的机器上将该插件导出为离线安装包。(可以从Github下载)

./logstash-5.6.4/bin/logstash-plugin prepare-offline-pack logstash-output-jdbc

将该安装包拷贝到目标机器上后,指定文件目录进行离线安装

../logstash-5.6.4/bin/logstash-plugin install file:///home/ubuntu/log_proxy_ip/logstash-offline-plugins-5.6.4.zip

2. jdbc driver 安装配置

因为我们准备使用的是 mysql,只需要安装 mysql-connector-java 并指定为 jdbc driver 即可。

如果安装有 maven 的话,可以配置 pom.xml 下载 mysql-connector-java 后拷贝到 logstash 目录下。

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.6</version>
</dependency>

或者可以从官方的链接中下载安装包 https://dev.mysql.com/downloads/connector/j/8.0.html

简单一点,从别的机器拷贝一个 jar 包也可以(可以从Github下载)

配置则在 logstash 的 /vender/ 目录下创建 /jar/jdbc/ 子目录,然后将对应的 jar 包拷贝到该目录即可。

若不想拷贝,也可以在 logstash 的配置文件中指定特定 jar 包的目录(设置 ·driver_jar_path 变量)

具体参考logstash-output-jdbc Github 仓库的 README 文档。

二.编写对应的配置文件

Nginx 的 access log 通常在 nginx.conf 中配置,使用包管理软件安装的 nginx 一般配置文件默认位于 /etc/nginx/ 目录下。

其中 http 下 access_log 配置项控制了 access log 的位置。默认情况下的配置如下:

http {
access_log /var/log/nginx/access.log
}

nginx access log 默认的格式为 combined format,logstash 的 grok 插件默认支持该格式。

nginx 的日志配置可以参考其官方文档

基于上面的假设,我们可以编写对应的 logstash 配置文件nginx_example.conf如下,其中 stdout { codec => rubydebug } 是让 logstash 将解析后的日志打印到 stdout。

input {
  file {
    path => ["/var/log/nginx/access.log"]
    type => "nginx"
  }
}

filter {
 grok {
   match => [ "message" , "%{COMBINEDAPACHELOG}+%{GREEDYDATA:extra_fields}"]
   overwrite => [ "message" ]
 }
}

output {
    stdout { codec => rubydebug }
}

使用 systemctl 启动 nginx,并使用该配置启动 logstash 查看是否成功简析 nginx 的 access log。

$ systemctl start nginx
$ ./logstash-5.6.4/bin/logstash -f nginx_example.conf

我在腾讯云的一台机器上搭建了该环境,在启动 nginx 和 logstash 后,马上就收到了各种扫描请求。(本地环境中测试可以手动 curl http://127.0.0.1 )

{
        "request" => "/10",
          "agent" => "\"lynx\"",
           "auth" => "-",
          "ident" => "-",
           "verb" => "POST",
        "message" => "61.216.152.133 - - [27/Oct/2018:19:34:50 +0800] \"POST /10 HTTP/1.1\" 404 178 \"-\" \"lynx\"",
           "type" => "nginx",
           "path" => "/var/log/nginx/access.log",
       "referrer" => "\"-\"",
     "@timestamp" => 2018-10-27T12:08:41.732Z,
       "response" => "404",
          "bytes" => "178",
       "clientip" => "61.216.152.133",
       "@version" => "1",
           "host" => "localhost.localdomain",
    "httpversion" => "1.1",
      "timestamp" => "27/Oct/2018:19:34:50 +0800"
}

可以看到 logstash 成功将 nginx 的 access log 解析成了结构化的数据。在此基础上,我们可以编写 logstash 配置文件,过滤特定的请求,并以特定的语句将感兴趣的数据写入到 mysql。

例如上面的流量经过分析是来着台湾ip的botnet 发起的扫描。https://www.abuseipdb.com/check/61.216.152.133

假设我们要记录所有使用 POST 方法访问该 URI 的请求,可以在 logstash output 中添加条件如下:

output {
    if [request] == "/10" {
       if [verb] == "POST" {
           stdout { codec => rubydebug }
       }
    }
}

jdbc 及数据库配置

上面我们已经验证了 logtash 可以成功的解析和记录特定的 nginx access log,接下来就是如何配置 mysql 及 logtash jdbc 将数据写入 mysql 了。

假设我们的 mysql server 位于本地服务器,并且用户为 root, 密码为空。

我们首先登录进 mysql ,创建用于记录 access log 的数据库和表。

$  mysql -uroot

创建数据库

create database nginx_access_log character set utf8;

创建表

记录恶意扫描的机器的 ip 和请求的 http method 以及 http url。

CREATE TABLE `access_log` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `clientip` varchar(135) NOT NULL DEFAULT '' COMMENT 'ip',
  `method` varchar(30),
  `url` varchar(191),
  `insert_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ip_UNIQUE` (`clientip`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8

配置 logstash-output-jdbc 插件

在 logtash 配置文件 output 部分中, 之前我们添加了请求方法为 POST,URL 为 /10 的过滤条件,我们在此基础上添加 jdbc 插件的配置。

根据 文档 ,我们需要设置的变量如下:

  • driver_class : 指定jdbc 的类型。
  • connection_string: jdbc 链接的 URI
  • statement: 类型为数组,其中第一个元素为未绑定参数的 SQL 语句,后续的元素为传入 SQL 语句的参数,可以为上面解析后的事件的属性(如 "request" 等),也可以为一个格式化字符串( "%host - %message" )
output {
    if [request] == "/10" {
       if [verb] == "POST" {
               jdbc {
                  driver_class => "com.mysql.jdbc.Driver"
                  connection_string => "jdbc:mysql://localhost:3306/nginx_access_log?user=root&password="
                  statement => ["REPLACE INTO access_log (clientip, method, url) values(?,?, ?)","clientip","verb", "request"]
          }
       }
    }
}

测试效果

最后完成的配置如下

input {
  file {
    path => ["/var/log/nginx/access.log"]
    type => "nginx"
  }
}

filter {
 grok {
   match => [ "message" , "%{COMBINEDAPACHELOG}+%{GREEDYDATA:extra_fields}"]
   overwrite => [ "message" ]
 }
}

output {
    if [request] == "/10" {
       if [verb] == "POST" {
               jdbc {
                  driver_class => "com.mysql.jdbc.Driver"
                  connection_string => "jdbc:mysql://localhost:3306/nginx_access_log?user=root&password="
                  statement => ["REPLACE INTO access_log (clientip, method, url) values(?,?, ?)","clientip","verb", "request"]
          }
       }
    }
}

使用上面的配置文件启动 logstash 后,手动触发写 mysql 操作。

$ curl -XPOST 127.0.0.1/10

登录 mysql 可以查看到测试结果

$ mysql -uroot nginx_access_log
mysql> select * from access_log;
+----+-----------+--------+------+---------------------+---------------------+
| id | clientip  | method | url  | insert_time         | update_time         |
+----+-----------+--------+------+---------------------+---------------------+
|  1 | 127.0.0.1 | POST   | /10  | 2018-10-27 20:56:45 | 2018-10-27 20:56:45 |
+----+-----------+--------+------+---------------------+---------------------+
1 row in set (0.00 sec)