60

搭建安全认证的ELK日志系统

 6 years ago
source link: http://www.freebuf.com/articles/security-management/179736.html?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

*本文原创作者:chuanwei,本文属FreeBuf原创奖励计划,未经许可禁止转载

前言

笔者使用ELK日志系统搭建了日志系统,由于ELK日志系统默认是没有认证功能,日志存储安全无法保证,ELK自身的漏洞也可能增加风险。本文中笔者采用nginx进行代理认证,写此文希望用自己的实验成果能帮到需要的人,提高安全管理人员安全意识。

1.背景

由于网络安全法要求信息系统运营单位的网络日志保留时间不低于6个月,在信息安全等级保护中也有相应要求,日志记录的保存也是执法检查部门近年的关注的重点,原因即是为了安全事件的事后追查,因此信息系统产生的各种日志必须进行安全的保存,规避违法违规风险,尽到作为信息系统运营单位安全保护责任。

因笔者在某政府单位驻场安全运维,该单位没有日志系统,因此决定使用著名的ELK日志系统搭建了日志系统,用于收集全网日志,包括网络设备syslog、操作系统syslog、apahce、tomcat、iis、was、weblogic等。由于ELK日志系统默认是没有认证功能,日志存储安全无法保证,ELK自身的漏洞也可能增加风险,很多人忽略了这一点,网上也没有文章强调认证。本文中我采用了nginx进行代理认证,写此文希望用自己的实验成果能帮到需要的人,提高安全管理人员安全意识。

2. 运行环境

windows 2008 R2 两台作为日志系统服务端, 安装Elasticsearch 5.6.9、Logstash 5.6.9、Nginx1.14.0、JDK1.8+。

centos 6.9 一台作为日志系统客户端,安装filebeat 5.6.9

3. 方案

filebeat: 作为客户端采集网站A 网站B….的apache、tomcat、nginx等中间件log文件,并配置tags(用于区分日志类型)和fields:service(用于区分日志来源网站建立不同索引)参数,不使用logstash作为客户端的原因是logstash太占用资源了,还需要java环境。filebeat接收syslog和filebeat发来的日志在filter中根据tags类型格式化,在output中根据fields:service配置索引输出到Elasticsearch不同网站建立不同索引。客户端不会直接请求Elasticsearch的9200端口,确保安全。

Elasticsearch: 安装到windows服务器,作日志存储处理,所在服务器开启防火墙:

策略1:允许集群节点之间tcp9200、tcp9300端口、ping通信。

策略2:允许filebeat客户端访问服务器tcp54320和udp514端口(logstash中定义)。策略3:允许管理员ip访问经过nginx代理认证后的head插件端口tcp19100(nginx中自定义)、Elasticsearch的tcp19200端口和kibana的tcp15601端口,方便管理员远程管理。所有策略配置好后客户端无法直接访问Elasticsearch的9100、9200、9300端口和kibana的5601端口,仅管理员可访问代理后的19100、19200、15601端口,对应9100、9200、5601端口。

Kibana: 安装到windows服务器,作为日志展示和查询。

Elasticsearch-head: Elasticsearch的图形管理插件。

Nginx: 安装到主windows服务器节点,因为Elasticsearch默认是无认证的,日志可被恶意删除,无论在内网还是公网都是非常危险的,因此有必要采用nginx进行反向代理认证。

Nssm: 将免安装的logstah、kibana、nginx安装为服务,配置为自动启动。

*对于性能有需求的可考虑使用redis,非本文关注重点,不再介绍了。

4. 下载必要软件和版本选择

*ELK5.6.9支持到windows server 2008 操作系统,更高版本需要windows server 2012操作系统。资源下载地址:

Elasticsearch 5.6.9: https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.6.9.zip

Logstash 5.6.9: https://artifacts.elastic.co/downloads/logstash/logstash-5.6.9.zip

filebeat 5.6.9 Linux: https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-5.6.9-x86_64.rpm

filebeat 5.6.9 Windows: https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-6.3.2-windows-x86_64.zip

Kibana 5.6.9: https://artifacts.elastic.co/downloads/kibana/kibana-5.6.9-windows-x86.zip

JDK(1.8以上): http://download.oracle.com/otn-pub/java/jdk/8u171-b11/512cd62ec5174c3487ac17c61aaa89e8/jdk-8u171-windows-x64.exe

elasticsearch-head: https://github.com/mobz/elasticsearch-head#running-with-built-in-server

nginx 1.14.0: http://nginx.org/download/nginx-1.14.0.zip

nssm: http://www.nssm.cc/release/nssm-2.24.zip

5. 安装ELK

5.1 Elasticsearch安装配置

解压修改配置文件即可运行。

请注意有一个坑:elasticsearch必须是两个组成集群,不然会出现错误,也可在同一台服务器运行两个elasticsearch,如果启动报错,是因为复制两个elasticsearch时data目录已有数据,造成冲突,清空data里面的数据即可启动两个elasticsearch。本文采用两台服务器搭建,配置如下:

主节点配置文件elasticsearch.yml

#禁用虚拟内存,提高性能
bootstrap.memory_lock: true
#节点名称自定义:
cluster.name: elasticsearch
#数据通信端口:
http.port: 9200
#监听网卡ip
network.host: 192.168.1.1
#是否是数据节点:
node.data: true
#关闭即可:
node.ingest: true
#是否是主节点,不定义的话先启动的是主节点:
node.master: true
#最大存储节点:
node.max_local_storage_nodes: 1
#节点名字自定义:
node.name: Win-Master-1
#数据文件路径
path.data: D:\elk\elasticsearch\data
path.logs: D:\elk\elasticsearch\logs
#节点间通信端口:
transport.tcp.port: 9300
#节点ip,节点之间要允许ping和9300端口通信
discovery.zen.ping.unicast.hosts: ["192.168.1.1", "192.168.1.2"]
#head插件相关:
http.cors.enabled: true
http.cors.allow-origin: "*"

备节点配置文件elasticsearch.yml

#禁用虚拟内存,提高性能
bootstrap.memory_lock: true
#节点名称自定义:
cluster.name: elasticsearch
#数据通信端口:
http.port: 9200
#监听网卡ip
network.host: 192.168.1.2
#是否是数据节点:
node.data: true
#关闭即可:
node.ingest: true
#是否是主节点,不定义的话先启动的是主节点:
node.master: false
#最大存储节点:
node.max_local_storage_nodes: 1
#节点名字自定义:
node.name: Win-salve-1
#数据文件路径
path.data: D:\elk\elasticsearch\data
path.logs: D:\elk\elasticsearch\logs
#节点间通信端口:
transport.tcp.port: 9300
#节点ip,节点之间要允许ping和9300端口通信
discovery.zen.ping.unicast.hosts: ["192.168.1.1", "192.168.1.2"]
#head插件相关:
http.cors.enabled: true
http.cors.allow-origin: "*"

安装服务

进入bin目录在cmd中运行:

elasticsearch-service.bat install
#图形服务管理工具或windows服务中启动服务
elasticsearch-service-mgr.exe

安装head插件

1.下载安装node.js ,网址: https://nodejs.org/en/ 2.nodejs安装目录下运行:

npm install -g grunt-cli

3.下载head插件,网址: https://github.com/mobz/elasticsearch-head/archive/master.zip 4.解压到elasticsearch下,自定义目录名称head,修改以下源码:

head/Gruntfile.js

connect: {
    server: {
        options: {
            port: 9100,
            hostname: '*',
            base: '.',
            keepalive: true
        }
    }
}

head/_site/app.js

this.base_uri = this.config.base_uri || this.prefs.get("app-base_uri") || "http://192.168.1.1:19100/elasticsearch/";
#这里的地址和第六章nginx代理配置对应。

5.2 logstash 安装配置

解压修改配置文件即可。

新建配置文件logstash.conf

input{
    stdin{
    }
    #配置端口,接收syslog日志:
    syslog {
        type => syslog
        port => 514
    }
    #配置端口,接收filebeat发来的日志。
    beats {
        port => 54320

    }
    #logstash做客户端时的配置举例,本方案不采用,故注释掉。
    #file {
    #    path => "D:/log/apache/access-*.log"
    #    type => "apache_access"
    #    start_position => "beginning"
    #    ignore_older => 0 #读取已有的日志文件
    #}

}
filter {
    #logstash做客户端时的配置举例,本方案不采用,故注释掉。
    #if [type] == "apache_access" {
    #    grok {
    #    match => {"message" => "%{COMBINEDAPACHELOG}" }
    #    }
    #}

    #filebeat做客户端时的配置
    #filter其实也可以不配置,但不利于根据字段搜索和分析日志。
    if "apache_access" in [tags] {
        grok {
        match => {"message" => "%{COMBINEDAPACHELOG}" }
        }
        #%{COMBINEDAPACHELOG}内置的apcahe日志匹配规则。
    }
    if "nginx_access" in [tags] {
        grok {
        match => {"message" => "%{COMBINEDAPACHELOG} %{QS:x_forwarded_for} %{HOSTNAME:server_name}" }
        }
        #为了便于搜索多二级域名的网站,这里修改nginx默认日志字段后,添加了{HOSTNAME:server_name}规则,nginx比apahce增加了一个{QS:x_forwarded_for}这里也加上了。
    }
    if "tomcat_access" in [tags] {
        grok {
        match => {"message" => "%{COMBINEDAPACHELOG}" }
        }
        #%{COMBINEDAPACHELOG}内置的日志匹配规则,可自定义。
    }

}

output{
    #logstash做客户端时的配置,输出到elasticsearch,,本方案不采用,故注释掉。
    #if [type] == "apache_access" {
    #    elasticsearch{
    #        hosts => ["10.0.21.194:9200"]
    #        index => "apache-%{+YYYY.MM.dd}"
    #    }
    #}

    #syslog到elasticsearch
    if [type] == "syslog" {
        elasticsearch{
            hosts => ["192.168.1.1:9200"]
            index => "syslog-%{+YYYY.MM.dd}"
        }
    }
    #根据filebeat中service不同域名建立不同索引,输出到elasticsearch
    if [fields][service] == "www.a.com" {
        elasticsearch{
            hosts => ["192.168.1.1:9200"]
            index => "www.a.com-%{+YYYY.MM.dd}"
        }
    }
    #根据filebeat中service不同域名建立不同索引,输出到elasticsearch
    if [fields][service] == "www.b.com" {
        elasticsearch{
            hosts => ["192.168.1.1:9200"]
            index => "www.c.com-%{+YYYY.MM.dd}"
        }
    }
    #其他日志
    elasticsearch{
        hosts => ["192.168.1.1:9200"]
        index => "other-%{+YYYY.MM.dd}"
    }
    #输出到屏幕
    stdout{
        codec=>rubydebug
    }
}

#提示:如果你想在业务服务器上使用logstash做客户端,但服务器是低版本java,logstash又需要以JDk1.8以上运行,可在logstash/startup.option配置文件中的set参数指定高版本JAVACMD路径,亲测可用。

安装服务

在bin目录新建一个logstash-start.bat文件写入:

logstash.bat logstash.conf

在cmd中使用nssm安装服务,选择logstash-start.bat路径,并配置启动依赖于“elasticsearch-service”服务(确保开启自启动正常):

nssm install logstash

关于filter的grok正则处理,单独说一下:

1.apahce日志Logstash 默认自带了 apache 标准日志的 grok 正则:

COMMONAPACHELOG %{IPORHOST:clientip} %{USER:ident} %{NOTSPACE:auth} \[%{HTTPDATE:timestamp}\] "(?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawrequest})" %{NUMBER:response} (?:%{NUMBER:bytes}|-)
COMBINEDAPACHELOG %{COMMONAPACHELOG} %{QS:referrer} %{QS:agent}

nginx 标准日志的 grok 正则定义是:

MAINNGINXLOG %{COMBINEDAPACHELOG}

2.对于 nginx 标准日志格式默认的nginx的log_format配置可能是下面这样:

log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" '
                '"$http_user_agent" "$http_x_forwarded_for"';

nginx比Apache多了一个 $http_x_forwarded_for 变量,所以 nginx 标准日志的 grok 正则定义是:

MAINNGINXLOG %{COMBINEDAPACHELOG} %{QS:x_forwarded_for}

3.为了便于搜索日志,在可日志中加一个server name字段,也即网站的域名,适用于网站群的情况,便于区分不同网站日志。

log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" '
                '"$http_user_agent" "$http_x_forwarded_for" $server_name';

添加server_name后的grok 正则定义:

MAINNGINXLOG %{COMBINEDAPACHELOG} %{QS:x_forwarded_for} %{HOSTNAME:server_name}

对于默认正则的修改可以这样在默认正则后面追加,前提是x_forwarded_for、server_name的正则在默认正则中是存在的:

match => {"message" => "%{COMBINEDAPACHELOG} %{QS:x_forwarded_for} %{HOSTNAME:server_name}" }

4.如何自定义规则。

默认的grok正则定义文件在下面路径:logstash/vendor/bundle/jruby/1.9/gems/logstash-patterns-core-0.3.0/patterns下。如果要自定义新的,在logstah.conf中指定规则文件路径即可,自定义规则优先级大于默认规则,举例如下:

grok {
            patterns_dir => "/usr/local/logstash/patterns"        //设置自定义正则路径
            match => {
                        "message" => "%{NGINXACCESS}"
                    }
    }

5.3 kibana 安装配置

解压修改配置文件即可。

修改配置文件config/kibana.yml

因为采用了nginx进行本地代理,只允许localhost本地访问即可。

server.port: 5601
server.host: "localhost"
elasticsearch.url: "http://192.168.1.1:9200"

安装服务

在bin目录新建一个kibana-start.bat文件写入:

kibana.bat

在cmd中使用nssm安装服务,选择kibana.bat路径,并配置启动依赖于“logstash”服务(确保开机自启动正常):

nssm install kibana

6. filebeat 安装配置

6.1 filebeat安装

仅介绍linux下安装,windows同理。

rpm -ivh filebeat-5.6.9-x86_64.rpm

6.2 修改配置文件filebeat.yml

filebeat.prospectors:
- input_type: log
  paths:
    - /usr/local/nginx/logs/*.log
  tags: ["nginx_access"]
  fields:
      service : www.a.com
output.logstash:
  hosts: ["192.168.1.1:54320"]
#参数说明:
#tages是标记日志类型,便于logstah进行filter格式化,对应logstah配置。
#service 是为了在logstash中根据不同网站发来的日志建立不同的Elasticsearch索引,方便日志查询,建议填写系统域名或别名,对应logstah配置。
#yml格式是根据缩进判断参数,相同缩进为一类参数,注意缩进。

6.3 自启动

systemctl enable filebeat或chkconfig filebeat on

6.4 logrotate 配置

由于我们将日志远程存储,本地日志就没有必要一直保留,占用存储空间,apahce自带日志分割功能,不再写具体方法了,默认情况下nginx、tomcat等产生的日志一直保存,我们可以使用logrotate分割日志,logrotate的配置文件在/etc/logrotate.conf。

举例配置如下:/usr/local/nginx/logs/*.log位置是新添加的,前面的可注释掉。

# see "man logrotate" for details
# rotate log files weekly
weekly

# keep 4 weeks worth of backlogs
rotate 4

# create new (empty) log files after rotating old ones
create

# use date as a suffix of the rotated file
dateext

# uncomment this if you want your log files compressed
compress

# RPM packages drop log rotation information into this directory
include /etc/logrotate.d

# no packages own wtmp and btmp -- we'll rotate them here
#/var/log/wtmp {
#    monthly
#    create 0664 root utmp
#     minsize 1M
#    rotate 1
#}

#/var/log/btmp {
#    missingok
#    monthly
#    create 0600 root utmp
#    rotate 1
#}
/usr/local/nginx/logs/*.log{
daily
rotate 90
missingok
notifempty
dateext
compress
delaycompress
create 600 root root
sharedscripts
postrotate
    if [ -f /usr/local/nginx/logs/nginx.pid ]; then
        kill -USR1 `cat /usr/local/nginx/logs/nginx.pid`
    fi
endscript
}
# system-specific logs may be also be configured here.

logrotate主要参数如下表:

参数 功能
compress 通过gzip 压缩转储以后的日志
nocompress 不需要压缩时,用这个参数
copytruncate 用于还在打开中的日志文件,把当前日志备份并截断
nocopytruncate 备份日志文件但是不截断
create mode owner group 转储文件,使用指定的文件模式创建新的日志文件
nocreate 不建立新的日志文件
delaycompress 和 compress 一起使用时,转储的日志文件到下一次转储时才压缩
nodelaycompress 覆盖 delaycompress 选项,转储同时压缩。
errors address 专储时的错误信息发送到指定的Email 地址
ifempty 即使是空文件也转储,这个是 logrotate 的缺省选项。
notifempty 如果是空文件的话,不转储
mail address 把转储的日志文件发送到指定的E-mail 地址
nomail 转储时不发送日志文件
olddir directory 转储后的日志文件放入指定的目录,必须和当前日志文件在同一个文件系统
noolddir 转储后的日志文件和当前日志文件放在同一个目录下
prerotate/endscript 在转储以前需要执行的命令可以放入这个对,这两个关键字必须单独成行
postrotate/endscript 在转储以后需要执行的命令可以放入这个对,这两个关键字必须单独成行
daily 指定转储周期为每天
weekly 指定转储周期为每周
monthly 指定转储周期为每月
rotate count 指定日志文件删除之前转储的次数,0 指没有备份,5 指保留5 个备份
tabootext [+] list 让logrotate 不转储指定扩展名的文件,缺省的扩展名是:.rpm-orig, .rpmsave, v, 和 ~ 
size size 当日志文件到达指定的大小时才转储,Size 可以指定 bytes (缺省)以及KB (sizek)或者MB (sizem).

7. 配置nginx反向代理及认证

7.1 修改配置文件nginx.conf

upstream kibana {
                server  127.0.0.1:5601;
        }

    upstream head {
                server  127.0.0.1:9100;
        }
    upstream elasticsearch {
                server  192.168.1.1:9200;            
        }
    server  {
                    listen          15601;
                    server_name     192.168.1.1;
                    location / {
                        proxy_set_header Host $host;
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

                        #配置帐号密码,在linux上使用命令,生成一个密码文件#命令:htpasswd -bc ./password admin 123456
                        #复制到windows上D:\elk\head-nginx\conf\password
                        auth_basic "login";#提示信息
                        auth_basic_user_file D:\elk\head-nginx\conf\password; #密码文件(写绝对路径,注意\n转义)
                        autoindex on;
                        proxy_pass http://kibana;

                    }
                    error_log           logs/kinaba._route_error.log;
                    access_log          logs/kinaba._route_access.log main;    

            }
    server  {
                    listen          19100;
                    server_name     192.168.1.1 localhost;

                    #配置帐号密码,同上
                    auth_basic "login";#提示信息
                    auth_basic_user_file D:\elk\head-nginx\conf\password; #密码文件(写绝对路径,别相对路径,注意转义)
                    autoindex on;

                    location / {
                        proxy_pass http://head/;
                        proxy_set_header Host $host;
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                    }
                    #这里使用http://192.168.1.1:19100/elasticsearch/请求9200服务,因此在head插件中配置elasticsearch服务路径是http://192.168.1.1:19100/elasticsearch/。
                    location /elasticsearch/ {
                        proxy_pass http://elasticsearch/;
                        proxy_set_header Host $host;
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                    }
                    error_log           logs/kinaba._route_error.log;
                    access_log          logs/kinaba._route_access.log main;    

            }

7.2 安装服务

在根目录新建一个nginx-start.bat文件写入:

nginx.exe

在cmd中使用nssm安装服务,选择nginx.exe路径:

nssm install nginx

参考连接:

https://blog.csdn.net/yx1214442120/article/details/55102298

https://blog.csdn.net/pilihaotian/article/details/52452010

*本文原创作者:chuanwei,本文属FreeBuf原创奖励计划,未经许可禁止转载


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK