YAMAHA RTXルーター機器のsyslogをElastic Stackで可視化してみた

# 前書き
2019年に投稿した内容では古くてそのままでは動作しなくなっているので2022年版として改めて検証も兼ねてメモを残しておきます。

https://qiita.com/ohhara_shiojiri/items/9b5aac3aa837f7e42112

# 背景

YAMAHA RTXルーター機器のログ情報をElastic Stackで可視化してみます。
syslog機能でデータ転送してそれをFluentdで受信して加工しElasticsearchにデータベース格納してkibanaでチャートやグラフで可視化する構成になります。
Docker環境で各サービスをお手軽に作成します。


# 動作環境

## ルーター機器

- YAMAHA RTX1200
- YAMAHA RTX1210

## ホスト側

- OS:Ubuntu Server 20.04.4LTS
- Docker CE version 20.10.16
- docker compose version v2.6.0

## コンテナ側

- Fluentd v1.14-1
- Elastic Stack 8.2.2

# ルータ機器

YAMAHA RTXシリーズ
逸般の誤家庭でもよく使われているYAMAHA のRTXシリーズのsyslogを前提条件とします。
また１台だけでなく複数台の利用も想定して、ファシリティ番号にタグ付けも行います。
なお、RTXシリーズのファイアウォール設定については割愛します。
syslog設定は以下の様になります。

```:
syslog host [syslogサーバー指定]
syslog facility [ファシリティ番号]
syslog notice on
```

syslogはUDP/514番を使用しますので、ファイアウォール設定を忘れずに。

# Docker環境の準備

以下のページにて紹介していますので参考にしてみて下さい。

- [Linuxサーバー環境のDockerインストールメモ - Qiita ](https://qiita.com/ohhara_shiojiri/items/486a54ad895d6bb3144e)

# Elastic Stack環境の準備

以下のページにて紹介していますので参考にしてみて下さい。

- [DockerでElastic Stack 8.2環境の構築](https://qiita.com/ohhara_shiojiri/items/0b45fd000103b7345073)


# FluentdのDocker環境作成

syslogのデータをFluentdで受信して加工する為にDockerでFluentdのサービスを作成します。

## Dockerfileの作成

データ加工に必要となるFluentdのPluginモジュールをDocker Imageにインストールする為にDockerfileを作成します。
作業用ディレクトリを作成して移動します。

```
$ mkdir -p fluentd
$ cd fluentd
```

Dockerfileを作成

```:fluentd/Dockerfile
FROM fluent/fluentd:v1.14-1

USER root

RUN fluent-gem install fluent-plugin-elasticsearch
RUN fluent-gem install fluent-plugin-filter-base64-decode
RUN fluent-gem install fluent-plugin-record-reformer
RUN apk add --no-cache --update --virtual .build-deps build-base ruby-dev
RUN apk add --no-cache geoip geoip-dev libmaxminddb automake autoconf libtool libc6-compat
RUN fluent-gem install fluent-plugin-geoip
RUN fluent-gem install fluent-plugin-parser
RUN fluent-gem install fluent-plugin-multi-format-parser
RUN fluent-gem install fluent-plugin-with-extra-fields-parser
RUN fluent-gem install fluent-plugin-config-expander
```

## docker compose ファイルの作成

Docker Composeでコンテナ環境を作成します。

```:fluentd/compose.yaml
services:
  fluentd:
    container_name: fluentd
    build: ./
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ./etc/fluent.conf:/fluentd/etc/fluent.conf
      - ./log:/fluentd/log
    ports:
      - "24224:24224"
      - "514:5140/udp"
    networks:
      - docker-elk_elk

networks:
  docker-elk_elk:
    external: true
```

ここではsyslogのホスト側UDP/514をFluentdのDocker Network側UDP/5140でデータ転送している事と別Docker ComposeプロジェクトのElastic StackのDocker Networkに接続しているのが要点です。

## Fluentdの設定

Fluentdの動作に関する設定ファイルを用意します。
設定用のディレクトリを作成します。

```
$ mkdir -p etc
$ mkdir -p log
```

Fluentdの設定ファイルを作成します。

```:etc/fluent.conf
<source>
  @type syslog
  port 5140
  bind 0.0.0.0
  format none
  tag raw.syslog
</source>

<match raw.syslog.**>
  @type copy
  <store>
    @type file
    path /fluent/log/syslog/syslog
    time_slice_format %Y%m%d
    <buffer time>
      @type file
      path /fluent/log/syslog/buffer
    </buffer>
  </store>
  <store>
    @type parser
    format multi_format
    key_name message
    remove_prefix raw
    add_prefix facility
    <pattern>
      format with_extra_fields
      base_format /^\[INSPECT\]\s+(?<target>.+)\[(?<direction>.+)\]\[(?<filter_num>\d+)\]\s+(?<proto>.+)\s+(?<src_ip>.+):(?<src_port>.+)\s+>\s+(?<dest_ip>.+):(?<dest_port>.+)\s+\((?<time>.+)\)$/
      time_format '%Y/%m/%d %H:%M:%S'
      extra_fields { "log_type": "inspect" }
    </pattern>
    <pattern>
      format with_extra_fields
      base_format /^\[INSPECT\]\s+(?<message>.+)\s+(?<src_ip>.+)\s+>\s+(?<dest_ip>.+)$/
      time_format '%Y/%m/%d %H:%M:%S'
      extra_fields { "log_type": "inspect" }
    </pattern>
    <pattern>
      format with_extra_fields
      base_format /^\[POLICY\]\s+(?<message>.+)\s+(?<src_ip>.+)\s+>\s+(?<dest_ip>.+)$/
      time_format '%Y/%m/%d %H:%M:%S'
      extra_fields { "log_type": "inspect" }
    </pattern>
    <pattern>
      format with_extra_fields
      base_format /^(?<target>.+)\s+Rejected\s+at\s+(?<direction>.+)\((?<filter_num>.+)\)\s+filter:\s+(?<proto>.+)\s+(?<src_ip>.+):(?<src_port>.+)\s+>\s+(?<dest_ip>.+):(?<dest_port>.+)$/
      extra_fields { "log_type": "reject" }
    </pattern>
    <pattern>
      format with_extra_fields
      base_format /^(?<target>.+)\s+Rejected\s+at\s+(?<direction>.+)\((?<filter_num>.+)\)\s+filter:\s+(?<proto>.+)\s+(?<src_ip>.+)\s+>\s+(?<dest_ip>.+)\s+:\s+(?<icmp_type>.+)$/
      extra_fields { "log_type": "reject" }
    </pattern>
    <pattern>
      format with_extra_fields
      base_format /^Rejected\s+directed\s+broadcast:\s+(?<proto>.+)\s+(?<src_ip>.+):(?<src_port>.+)\s+>\s+(?<dest_ip>.+):(?<dest_port>.+)$/
      extra_fields { "log_type": "broadcast" }
    </pattern>
    <pattern>
      format with_extra_fields
      base_format /^Rejected\s+directed\s+broadcast:\s+(?<proto>.+)\s+(?<src_ip>.+)\s+>\s+(?<dest_ip>.+)\s+:\s+(?<icmp_type>.+)$/
      extra_fields { "log_type": "broadcast" }
    </pattern>
    <pattern>
      format with_extra_fields
      base_format /^Logout\s+from\s+(?<proto>.+):\s+(?<ip>.+)$/
      extra_fields { "log_type": "console_logout" }
    </pattern>
    <pattern>
      format with_extra_fields
      base_format /^Login\s+succeeded\s+for\s+(?<proto>.+):\s+(?<ip>.+)$/
      extra_fields { "log_type": "console_login" }
    </pattern>
    <pattern>
      format with_extra_fields
      base_format /^\[(?<proto>.+)\]\s+(?<tunnel>.+)\s+connected\s+from\s+(?<src_ip>.+)$/
      extra_fields { "log_type": "tunnel_connect" }
    </pattern>
    <pattern>
      format with_extra_fields
      base_format /^\[(?<proto>.+)\]\s+(?<tunnel>.+)\s+disconnect\s+tunnel\s+\d+\s+complete$/
      extra_fields { "log_type": "tunnel_disconnect" }
    </pattern>
    <pattern>
      format with_extra_fields
      base_format /^(?<msg>.+)$/
      extra_fields { "log_type": "other" }
    </pattern>
  </store>
</match>

<match facility.syslog.**>
  @type record_reformer
  tag yamaha_rtx
  <record>
    facility ${tag_parts[-2]}
  </record>
</match>

<filter yamaha_rtx.**>
  @type geoip
  geoip_lookup_keys src_ip
  <record>
    location      '[${location.latitude["src_ip"]},${location.longitude["src_ip"]}]'
    country_name  ${country.names.en["src_ip"]}
  </record>
  skip_adding_null_record true
</filter>

<match yamaha_rtx.**>
  @type elasticsearch
  host elasticsearch
  port 9200
  user elastic
  password `作成したelasticsearchのパスワード`
  verify_es_version_at_startup false
  default_elasticsearch_version 8
  logstash_format true
  logstash_prefix yamaha_rtx
  include_tag_key true
  tag_key @log_name
  buffer_type memory
</match>
```

設定については2019年に投稿した内容を一部修正しています。
現在ではElasticsearchに接続する際にはバージョン指定や認証情報も必要ですので追加指定しています。

## FluentdのDocker Imageの作成

Docker ComposeでFluentdのDocker Imageをビルドして作成します。

```
$ docker compose build
$ docker images
REPOSITORY                      TAG                 IMAGE ID       CREATED         SIZE
fluentd_fluentd                 latest              f27d16230a0b   1 days ago      432MB
```

無事にFluentdのDocker Imageが作成出来たらDockerコンテナを起動してみます。

```
$ docker compose up -d
$ docker compose ps
NAME                COMMAND                  SERVICE             STATUS              PORTS
fluentd             "tini -- /bin/entryp…"   fluentd             running             0.0.0.0:514->5140/udp, 0.0.0.0:24224->24224/tcp, :::514->5140/udp, :::24224->24224/tcp
```

これで各サービスが起動していますのでルーター機器のログ情報がsyslog出力されてFluentdで受信して加工されてElasticsearchにデータベース格納される様になります。
Elastic Stachの画面から受信状況を確認出来ると思います。あとはKibanaでインデックスパターンを作成してチャートやグラフを作成して可視化してみましょう。