1. 前言

filebeat 6.6.2 nginx module自定义字段
filebeat提供了多种Module预制模块,简化了各种日志的格式化
在nginx中默认的字段并不满足实际需求,例如我们需要记录额外的Nginx字段
例如 请求时间、后端响应时间、主机头等信息
那么在filebeat的nginx module中需要同步定义

2. rpm模式安装filebeat

curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-6.6.2-x86_64.rpm
sudo rpm -vi filebeat-6.6.2-x86_64.rpm

3. Nginx 自定义Log格式化

nginx原始log格式化定义:

        #log format
        log_format access '$remote_addr - $remote_user [$time_local] "$request" '
                            '$status $body_bytes_sent "$http_referer" '
                            '"$http_user_agent" "$http_x_forwarded_for" '

nginx自定义格式化log:

        #log format
        log_format access '$remote_addr - $remote_user [$time_local] "$request" '
                            '$status $body_bytes_sent "$http_referer" '
                            '"$http_user_agent" "$http_x_forwarded_for" '
                            '"$http_x_real_ip" "$server_addr" "$host" '
                            '$request_time $upstream_response_time "$upstream_addr" '
                            '"$time_iso8601"';

4. 启用filebeat的nginx module

执行

filebeat modules enable nginx

filebeat nginx module 增加字段
文件 /usr/share/filebeat/module/nginx/access/ingest/default.json 中
原始:

    "grok": {
      "field": "message",
      "patterns":[
        "\"?%{IP_LIST:nginx.access.remote_ip_list} - %{DATA:nginx.access.user_name} \\[%{HTTPDATE:nginx.access.time}\\] \"%{GREEDYDATA:nginx.access.info}\" %{NUMBER:nginx.access.response_code} %{NUMBER:nginx.access.body_sent.bytes} \"%{DATA:nginx.access.referrer}\" \"%{DATA:nginx.access.agent}\""
        ],
      "pattern_definitions": {
        "IP_LIST": "%{IP}(\"?,?\\s*%{IP})*"
      }

自定义修改:

    "grok": {
      "field": "message",
      "patterns":[
        "\"?%{IP_LIST:nginx.access.remote_ip_list} - %{DATA:nginx.access.user_name} \\[%{HTTPDATE:nginx.access.time}\\] \"%{GREEDYDATA:nginx.access.info}\" %{NUMBER:nginx.access.response_code} %{NUMBER:nginx.access.body_sent.bytes} \"%{DATA:nginx.access.referrer}\" \"%{DATA:nginx.access.agent}\" \"%{DATA:nginx.access.xff}\" \"%{DATA:nginx.access.x_real_ip}\" \"%{DATA:nginx.access.server_addr}\" \"%{DATA:nginx.access.host}\" %{DATA:nginx.access.request_time} %{DATA:nginx.access.upstream_response_time} \"%{DATA:nginx.access.upstream_addr}\" \"%{DATA:nginx.access.time_iso8601}\""
        ],
      "pattern_definitions": {
        "IP_LIST": "%{IP}(\"?,?\\s*%{IP})*"
      }

如果定制自己的字段
可以使用http://grokdebug.herokuapp.com/ 在线工具验证grok规则
可以使用Json工具处理转义问题

文件 /etc/filebeat/fields.yml 中
找到nginx字段配置

            - name: agent
              type: text
              description: >
                Contains the un-parsed user agent string. Only present if the user
                agent Elasticsearch plugin is not available or not used.

在后面增加

            - name: xff
              type: group
              description: >
                http_x_forwarded_for.
            - name: x_real_ip
              type: group
              description: >
                http_x_real_ip.
            - name: server_addr
              type: group
              description: >
                server_addr 服务器地址.
            - name: host
              type: group
              description: >
                host http_host http主机头.
            - name: request_time
              type: group
              description: >
                request_time 请求时间.
            - name: upstream_response_time
              type: group
              description: >
                upstream_response_time 后端响应时间.
            - name: upstream_addr
              type: group
              description: >
                upstream_addr 后端地址.
            - name: time_iso8601
              type: group
              description: >
                time_iso8601 iso8601格式时间.

5. Filebeta中 nginx 日志路径定义

在文件 /etc/filebeat/modules.d/nginx.yml 中修改日志路径

- module: nginx
  # Access logs
  access:
    enabled: true

    # Set custom paths for the log files. If left empty,
    # Filebeat will choose the paths depending on your OS.
    var.paths: ["/data/wwwlogs/*.log*"]

  # Error logs
  error:
    enabled: true

    # Set custom paths for the log files. If left empty,
    # Filebeat will choose the paths depending on your OS.
    var.paths: ["/usr/local/nginx/logs/error.log*"]

6. 扩展

6.1 解决增加字段后Kibana中不显示

  • 在Kibana的 Dev Tools中执行
DELETE _ingest/pipeline/filebeat-6.6.2-nginx-access-default
  • 字段出来以后有黄色小三角,刷新一下index 的字段缓存就好了
  • 重启下Filebeat
    方法来源

隐藏nginx、openresty版本号

隐藏nginx、openresty的版本号有什么用?
假设一个场景,nginx被爆出0.9-1.5的版本被爆出一个0day漏洞,
攻击者会先大量扫描匹配的nginx版本,然后实施攻击。
如果事先隐藏了会降低第一时间被攻击的风险

在 http {} 中间配置增加

server_tokens off;

在http头中从原来的
Server: nginx/1.0.15 变为 Server: nginx
Server: openresty/1.11.2.3 变为 Server: openresty

nginx 日志格式化完整增强版

本完整增强版主要解决了后端执行时间的记录、哪台后端处理的、日志集中化后日志来自于哪台服务器ip、cdn传过来的客户端ip等扩展等问题。

在默认的nginx日志上,扩展增加了http头中代理服务器ip($http_x_forwarded_for)、
http头中cdn保存的客户端用户真实IP($http_x_real_ip)、服务端ip($server_addr)、http头中host主机($host)、
请求时间($request_time)、后端返回时间($upstream_response_time)、后端地址($upstream_addr)、
URI($uri)、ISO 8601标准时间($time_iso8601)

#log format
log_format access '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'"$http_x_real_ip" "$server_addr" "$host" '
'$request_time $upstream_response_time "$upstream_addr" '
'"$time_iso8601"';

nginx日志滚动切割

繁忙的nginx服务器每天都会产生大量的web日志,所以每天需要切割。
每天切割的日志需要保留一段时间,更老的日志需要删除,专业叫法叫做日志滚动类似于视频监控,
所需要保留一定时间的日志还需要删除更老的日志。

很多人喜欢手动用bash shell去写nginx的日志切割滚动配合定时计划任务执行执行。
其实用系统自带的logrotate更好。
新建文件

/etc/logrotate.d/nginx

写入

/data/wwwlogs/*.log {
    #设置权限
    su root www
    #以天为周期分割日志
    daily
    #最小 比如每日分割 但如果大小未到 1024M 则不分割
    minsize 1024M
    #最大 当日志文件超出 2048M 时,即便再下一个时间周期之前 日志也会被分割
    maxsize 2048M
    #保留七天
    rotate 7
    #忽略错误
    missingok
    #如果文件为空则不分割 not if empty
    notifempty
    #以日期为单位
    dateext
    #以大小为限制做日志分割 size 会覆盖分割周期配置 1024 1024k 1024M 1024G
    size 1024M
    #开始执行附加的脚本命令 nginx写日志是按照文件索引进行的 必须重启服务才能写入新日志文件
    sharedscripts
    postrotate
    if [ -f /usr/local/nginx/logs/nginx.pid ]; then
    #重启nginx服务
    kill -USR1 `cat /usr/local/nginx/logs/nginx.pid`
    fi
    endscript
}

elastic stack elk日志系统

采集的日志需要格式化格式,要么在采集端做,要么在入库elasticsearch的时候做。
在nginx中直接配置输出的日志就是json格式,可以减少格式化日志的cpu开销
在日志采集端,用filebeat、或者logstash作为日志采集工具可以不做任务的格式化处理,
仅仅采集json格式的文本即可。

log_format logstash_json '{"@timestamp":"$time_iso8601",'
'"host":"$server_addr",'
'"clientip":"$remote_addr",'
'"remote_addr":"$remote_addr",'
'"http_x_forwarded_for":"$http_x_forwarded_for",'
'"http_x_real_ip":"$http_x_real_ip",'
'"http_cf_connecting_ip":"$http_cf_connecting_ip",'
'"size":$body_bytes_sent,'
'"responsetime":$request_time,'
'"upstreamtime":"$upstream_response_time",'
'"upstreamhost":"$upstream_addr",'
'"http_host":"$host",'
'"request":"$request",'
'"url":"$uri",'
'"referer":"$http_referer",'
'"agent":"$http_user_agent",'
'"status":"$status"}';

nginx反向代理

listen 80;
#listen [::]:80;
server_name proxy.iamle.com;

location / {
#auth_basic "Password please";
#auth_basic_user_file /usr/local/nginx/conf/htpasswd;
proxy_pass http://127.0.0.1:5601/;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

nginx反向代理的时候要支持后端服务器为DDNS动态域名

server_name proxy.iamle.com;
resolver 1.1.1.1 valid=3s;
set $HassHost "http://backend.iamle.com:999";
location / {
    #auth_basic "Password please";
    #auth_basic_user_file /usr/local/nginx/conf/htpasswd;
    proxy_pass $HassHost;
    proxy_redirect off;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-NginX-Proxy true;
}

nginx 反向代理 WebScoket

location /api/ {
        proxy_pass http://webscoket:80/;
        # WebScoket Support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        #proxy_set_header Origin xxx;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;
}

nginx代理设置php pm status

php-fpm.conf设置

pm.status_path = /phpfpm-status-www

phpfpm.conf

server
{
listen 80;
server_name localhost;
location ~ ^/(phpfpm-status-www|phpstatuswww)$
{
fastcgi_pass unix:/tmp/php-cgi.sock;
include fastcgi.conf;
fastcgi_param SCRIPT_FILENAME $fastcgi_script_name;
}
}

nginx 域名SEO优化 301

把 iamle.com 默认 301到 www.iamle.com

listen 80;
server_name www.iamle.com iamle.com;
if ($host != 'www.iamle.com' ) {
rewrite ^/(.*)$ https://www.iamle.com/$1 permanent;
}

nginx 全站http跳转https

server{
listen 80;
server_name www.iamle.com;
return 301 https://www.iamle.com$request_uri;
}

server {

listen 443 ssl http2;
ssl    on;
ssl_certificate         /usr/local/nginx/conf/ssl/www.iamle.com.crt;
ssl_certificate_key     /usr/local/nginx/conf/ssl/www.iamle.com.key;
ssl_session_cache           shared:SSL:10m;
ssl_session_timeout         10m;
ssl_session_tickets         off;

# intermediate configuration. tweak to your needs.
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
    ssl_prefer_server_ciphers on;

# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
#add_header Strict-Transport-Security max-age=15768000;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";

# OCSP Stapling ---
# fetch OCSP records from URL in ssl_certificate and cache them
ssl_stapling on;
ssl_stapling_verify on;

server_name www.iamle.com;
#  ....
}

XSS、Ifram

add_header X-Frame-Options SAMEORIGIN;                                                                                                                        
add_header X-Content-Type-Options nosniff;                          
add_header X-Xss-Protection "1; mode=block";

nginx http2 openssl 支持情况介绍

Supporting HTTP/2 for Website Visitors

ssl https 证书配置生成巩固

Mozilla SSL Configuration Generator

nginx+php 开启OPCACHE 并且使用软连接发布

软连接的方式发布新版本php代码,但是因为OPCACHE未及时更新某些时候会导致502错误
2个方式,方式1直接在nginx这里自动更新最新路径,方式2刷新php-fpm的缓存
下面是方式1

        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;

Cache invalidation for scripts in symlinked folders
Symfony Configuring a Web Server

介绍

ELK是业界标准的日志采集,存储索引,展示分析系统解决方案
logstash提供了灵活多样的插件支持不同的input/output
主流使用redis/kafka作为日志/消息的中间环节
如果已有kafka的环境了,使用kafka比使用redis更佳
以下是一个最简化的配置做个笔记,elastic官网提供了非常丰富的文档
不要用搜索引擎去搜索,没多少结果的,请直接看官网文档

采用的ELK/kafka版本

elasticsearch-2.x
logstash-2.3
kibana-4.5.1

Kafka 0.9.0.1

应用/网络 环境

Nginx机
10.0.0.1

Kafka群集
10.0.0.11
10.0.0.12
10.0.0.13

ElasticSearch机
10.0.0.21

整体说明

数据流向

日志/消息整体流向
logstash => kafka => logstash => elasticsearch => kibana

安装

elk所有安装都可以使用rpm二进制包的方式,增加elastic官网的仓库repo就可以用yum安装了

elasticsearch看这里
https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-repositories.html

logstash看这里
https://www.elastic.co/guide/en/logstash/current/installing-logstash.html

kibana看这里
https://www.elastic.co/guide/en/kibana/current/setup.html

安装概览

nginx机 10.0.0.1
运行nginx的日志格式化为json
运行logstash输入input从nginx json,输出output到kafka

kafka群集 10.0.0.11 10.0.0.12 10.0.0.13
kafka群集Topic为logstash

elasticsearch机10.0.0.21
运行elasticsearch
运行logstash输入input从kafka,输出output到elasticsearch

Nginx机

nginx日志格式化为json

在nginx的 http{} 中定义一个名为logstash_json格式化,格式化日志为json

log_format logstash_json '{ "@timestamp": "$time_local", '
'"@fields": { '
'"remote_addr": "$remote_addr", '
'"remote_user": "$remote_user", '
'"body_bytes_sent": "$body_bytes_sent", '
'"request_time": "$request_time", '
'"status": "$status", '
'"request": "$request", '
'"request_method": "$request_method", '
'"http_referrer": "$http_referer", '
'"body_bytes_sent":"$body_bytes_sent", '
'"http_x_forwarded_for": "$http_x_forwarded_for", '
'"http_user_agent": "$http_user_agent" } }';

在server{} 中增加记录logstash_json日志,可以用原有的日志输出共存

access_log /data/wwwlogs/iamle.log log_format;
access_log /data/wwwlogs/nginx_json.log logstash_json;

logstash日志采集配置

/etc/logstash/conf.d/nginx.conf

input {
file {
path => "/data/wwwlogs/nginx_json.log"
codec => "json"
}
}
filter {
mutate {
split => [ "upstreamtime", "," ]
}
mutate {
convert => [ "upstreamtime", "float" ]
}
}
output {
kafka {
bootstrap_servers => "10.0.0.11:9092"
topic_id => "logstash"
compression_type => "gzip"
}
}

Kafka群集

新建一个Topic

新建一个Topic叫做
logstash

Topic
每条发布到Kafka集群的消息都有一个类别,这个类别被称为topic。(物理上不同topic的消息分开存储,逻辑上一个topic的消息虽然保存于一个或多个broker上但用户只需指定消息的topic即可生产或消费数据而不必关心数据存于何处)

ElasticSearch机

logstash把数据从kafka存到elasticsearch的配置

其中选取kafka群集任意一个有zk的ip做连接使用
topic_id就是kafka中设置的topic logstash
/etc/logstash/conf.d/logstashes.conf

input {
kafka {
zk_connect => "10.0.0.13:2181"
topic_id => "logstash"
}
}
filter {
mutate {
split => [ "upstreamtime", "," ]
}
mutate {
convert => [ "upstreamtime", "float" ]
}
}
output {
elasticsearch {
hosts => ["10.0.0.21"]
index => "logstash-iamle-%{+YYYY.MM.dd}"
document_type => "iamle"
workers => 5
template_overwrite => true
}
}

补充说明

以上是主要的配置,就差kibana的查看/展示了

kibana

我这里kibana和elasticsearch是同一台机器
官方yum方式安装的kibana配置文件在
/opt/kibana/config/kibana.yml
需要改2个地方,监听端口和es的连接信息

server.host: "10.0.0.21"
elasticsearch.url: "http://10.0.0.21:9200"

启动kibana /etc/init.d/kibana start后可以通过 http://10.0.0.21:5601 访问

kibana的使用的多看官网文档,网上中文的资料不多,关于elk有一本饶琛琳写的
ELKstack 中文指南
https://www.gitbook.com/book/chenryn/kibana-guide-cn/details
kibana Discover 过滤静态文件
NOT \/static AND NOT \/upload\/

elasticsearch

官方yum方式安装的elasticsearch配置文件在
/etc/elasticsearch/elasticsearch.yml
需要配置下监听ip,默认是127.0.0.1

network.host: 10.0.0.21
path.data: /data

elasticsearch安装了head插件后可以看到es状态
http://10.0.0.21:9200/_plugin/head/

安全问题

特别要注意elk所有软件的端口监听,切勿暴露监听到公网上去,另外即便是内网你也得注意配置内网的访问限制