Quantcast
Channel: mattintosh note
Viewing all articles
Browse latest Browse all 891

DNS サーバのログを Elasticsearch と Kibana で可視化する

$
0
0

自宅で DNSサーバに Unbound を使っているのだけど立ててるだけで特に監視していないので何か遊んでみようと考えた。

久しぶりに Fluentd を使おうと思ったらバージョンが変わっていて conf の書式にハマった。Elasticsearch は Raspberry Piで動かしているが Unbound のログの量が多いので調整はまだ続きそう…(´・ω・`)

  • Unbound 1.9.0
  • fluentd 1.4.0
  • Python 3.5.3
  • Elasticsearch 6.5.2
  • Kibana 6.5.2

Fluentd tail Input Plugin で Unbound のログを解析する

まずは Unbound のログの解析。ログファイルから tail プラグインを使って読み込む。

https://docs.fluentd.org/v1.0/articles/in_tail

Unbound のログは log-queriesを有効にしておいて infoqueryreplyの区別が付くようにしておく。

/etc/unbound/unboud.conf

log-queries: yes

Unbound のログは以下のような書式になる。ここからタイムスタンプ、クライアント、ホストを取り出していく。

/var/log/unbound/unbound.log

[1551614551] unbound[24609:0] query: 192.168.1.10 ssl.gstatic.com. A IN

fluent.conf

Unbound のログをすべて解析するわけではないので、パターンに一致しない行が pattern not match でダラダラと出てくるので @log_level errorで出力を抑制しておく。

<source>
  @type       tail
  @log_level  error
  tag         unbound.log
  path        /var/log/unbound/unbound.log
  pos_file    /tmp/fluentd_unbount.log.pos
  <parse>
    @type       regexp
    expression  ^\[(?<time>[0-9]+)\] unbound\[.+\] query: (?<client>[^ ]+) (?<host>[^ ]+)\. A IN$
    time_key    time
    time_type   unixtime
  </parse>
</source>

# 出力確認用
<filter unbound.log>
  @type stdout
</filter>

🤔 @timestamp フィールドを作るか作らないか

Elasticsearch プラグインlogstash_formatを有効にする場合、プラグイン側で自動的に @timestampフィールドを作成してくれるのでここで用意しておく必要はない。外部フィルターで時間に対して何かしらの処理をしたい場合や logstash_formatを使わない場合はここで @timestampフィールドを作成しておくといいかもしれない。

🤔 時間フィールドが消えてしまう問題

time_keyで時間として指定したフィールドはデフォルトだと消えてしまうので残しておきたい場合は keep_time_keyを有効にする。

🤔 数値が数値型ではなく文字列型になってしまう問題

キーの型を指定したい場合は typesキー名:型と指定する必要があるようだ。ここでは数値型にしたいので @timestamp:integerとする。

tail Input Plugin の出力結果

2019-03-03 21:02:31.000000000 +0900 unbound.log: {"client":"192.168.1.10","host":"ssl.gstatic.com"}

exec_filter でホスト名から国や緯度経度情報を取得するフィルターを作る

以前は GeoIPモジュールを使っていたけど今回は GeoLite2-City.mmdb を使いたかったので maxminddbモジュールを使うことにした。GeoIPだと record_by_name()でホスト名から情報を拾えるんだけど maxminddbget()で IP アドレスを渡すくらいしかできないので socket.gethostbyname()を通して IP アドレスを渡している。

#!/usr/bin/env python3import sys
import json
import socket
import maxminddb
reader = maxminddb.open_database('GeoLite2-City.mmdb')
for line in sys.stdin:
    d = json.loads(line)
    g = reader.get(socket.gethostbyname(d['host']))
    d.update({
        'country': {
            'iso_code': g['country']['iso_code'],
        },
        'location': {
            'lat': g['location']['latitude'],
            'lon': g['location']['longitude'],
        },
    })
    sys.stdout.write(json.dumps(d))

fluent.conf

GeoIP フィルターとやりとりする部分を書いていく。バッファはメモリに配置。<format></format><parse></parse>が以前のバージョンになかったので困った。

Elasticsearch プラグインlogstash_formatを使う場合、外部フィルターから返ってきた JSONから再度時間を抽出する必要がある。

<match unbound.log>
  @type   exec_filter
  tag     exec.unbound
  command /usr/bin/python3 geoip.py
  <format>
    @type json
  </format>
  <parse>
    @type json
  </parse>
  <inject>
    time_key  @timestamp
    time_type unixtime
  </inject>
  <buffer>
    @type memory
  </buffer>
</match>

Elasticsearch のマッピングをする

Fluentd から Logstash 形式でデータを投入する場合、日時でインデックスが作成されるが、マッピングの設定ができない。今回は緯度経度情報を扱うため、geo_pointの指定が必須になる。そこで、テンプレートを用意して自動的に適用されるようにしておく。データ量が多いので refresh_intervalをデフォルトの 1sから 30sに変更。string 型のデータは解析する必要もないので keywordで入るようにしておく。

@timestampepoch_secondを使う場合はここで設定しておけばよい。

PUT _template/unbound{"index_patterns": "unbound-*",
  "settings": {"number_of_shards": 1,
    "number_of_replicas": 0,
    "refresh_interval": "30s"
  },
  "mappings": {"_doc": {"_all": {"enabled": false},
      "dynamic_templates": [{"strings": {"match_mapping_type": "string",
            "mapping": {"type": "keyword"
            }}},
        {"geo_point": {"match": "location",
            "mapping": {"type": "geo_point"
            }}}]}}}

Fluentd から Elasticsearch にデータを投入する部分を書く

データ量が多いせいか1時間ほどで Elasticsearch にデータが入らなくなってしまうので request_timeoutをデフォルトの 5sから 30sに増やしてある。(他は調整中)

今回は logstash_formatを有効にするのでレコードの @timestampフィールドは自動的に作成される。

<match exec.unbound>
  @type           elasticsearch
  hosts           localhost:9200
  type_name       _doc
  logstash_format true
  logstash_prefix unbound
  request_timeout 30s
  <buffer>
    flush_thread_count  4
    chunk_limit_records 200
  </buffer>
</match>

Elasticsearch に投入されたデータ

{"_index": "unbound-2019.03.03",
  "_type": "_doc",
  "_id": "bbb0Q2kBVp0AiN9Y-bd0",
  "_score": 1,
  "_source": {"client": "127.0.0.1",
    "location": {"lon": -97.822,
      "lat": 37.751},
    "host": "www.elastic.co",
    "country": {"iso_code": "US"
    },
    "@timestamp": "2019-03-03T23:28:41.234223023+09:00"
  },
  "fields": {"@timestamp": ["2019-03-03T14:28:41.234Z"
    ]}}

Kibana でダッシュボードを作る

Fluentd から送られてきたデータを Coordinate Map、Heat Map、Line で可視化する。

こうして見てみるとただブラウザで調べごとをしていたりするだけでも案外いろんな国にまで行っているのだなぁと感じる。

f:id:mattintosh4:20190304000151p:plain
Kibana - Dashboard

しばらく監視してみておかしなサイトに繋ぎに行ってないかとか発見出来れば面白いかな。

プロキシサーバのログも解析したいけど今日はもう疲れたのでまた今度にする…( ˘ω˘)スヤァ


Viewing all articles
Browse latest Browse all 891

Trending Articles