syslog-ng

提供: ArchWiki
2023年12月7日 (木) 02:48時点におけるKgx (トーク | 投稿記録)による版 (序文を更新)
ナビゲーションに移動 検索に移動

関連記事

syslog-ng は、強力なフィルターディレクティブに基づいて、ソースからログメッセージを取得し、宛先に転送できる syslog 実装です。その起源は syslog ですが、これは非常に汎用的なログ管理ツールであり、構造化および非構造化ログメッセージを使用し、必要に応じて解析および変換することができます。

ノート: systemd にアップグレードした後は、systemd の journal でログを見ることができるので、journalctl で足りる場合は、syslog-ng は必要なくなりアンインストールすることができます。

概要

syslog-ng は定義済みの 'source' からのメッセージを受け取って、強力な filter ディレクティブに基づいて、適当な destination に転送します。標準的なシンプルな設定だと、syslog-ng は3つの source からメッセージを読み取ります:

  1. デフォルトの /dev/log デバイス (ほとんどのログはここに送られます)
  2. syslog-ng の"内部的な"ログメッセージ
  3. /proc/kmsg カーネルメッセージ

受信元は "source" ディレクティブを使って定義します。受信されたメッセージは次に定義済みのフィルター ("filter" キーワード) によってフィルタリングされ、送信元のプログラムやログレベルにあわせて、適当な "destination" に送信されます。送信先としてはログファイル (例: /var/log/messages.log) や、コンソールやリモートサーバーにメッセージを表示するなどが考えられます。中心的な関数は log です。この関数は特定の source に対してどのフィルターを適用し、作成されたメッセージをどこに送信すればいいのか定義します。

syslog-ng.service サービスファイルを使って syslog-ng を有効化します。systemd 216 から、メッセージはデフォルトでは syslog に転送されなくなりました。syslog-ng 3.6 のリリースまで Syslog-ng は journald に対応していませんでした。systemd 216 以上で syslog-ng を使用するには /etc/systemd/journald.conf を編集して ForwardToSyslog=yes オプションを設定する必要があったのです。

最新の syslog-ng を使っている場合は、オプションを変更する必要はありません。syslog-ng が journal からメッセージを引っ張ってきてくれます。ForwardToSyslog=yes と設定していた場合は ForwardToSyslog=no に戻してください。ソケットの関連付けによるオーバーヘッドと ログに無駄なエラーメッセージが残る のを防ぐためです。一方で、ログを二度保存したくなく journaldStorage=none にする場合、ForwardToSyslog=yes を設定する必要があります。これによって syslog-ng は 'journald' の journal ファイルに従うようになります。

インストール

syslog-ng パッケージをインストールしてください。syslog-ng を使用開始するには、syslog-ng@default.service起動有効化してください。

Source

syslog-ng は source からログメッセージを受信します。source を定義するときは以下の構文に従う必要があります:

source <identifier> { source-driver(params); source-driver(params); ... };

identifier と source-driver については 公式マニュアル で読むことができます。マニュアルにしたがって上記の設定ファイルを説明します。unix-stream() source-driver は指定された AF_UNIX ソケットを開いてメッセージが来るのを待ちます。internal() source-driver は syslog-ng によって生成されたメッセージを取得します。

したがって、以下の設定の意味は: src/dev/log ソケットと syslog-ng からのメッセージを取得する、となります。

source src { unix-stream("/dev/log"); internal(); };

カーネルはログメッセージを /proc/kmsg に送信して file() ドライバーはファイルからログメッセージを読み取ります。したがって、以下の設定の意味は: kernsrc は /proc/kmsg ファイルからメッセージを取得する、となります。

source kernsrc { file("/proc/kmsg"); };

デフォルト設定では syslog-ng の後に、以下のように source が定義されています:

source src { unix-stream("/dev/log"); internal(); pipe("/proc/kmsg"); };

pipe("/proc/kmsg") によってメッセージを読み込むことでパフォーマンスには良い影響がありますが、読み書きモードで開くのでセキュリティ上問題になります。このことは syslog-ng admin guide のセクション 3.3.3 で触れられています:

"パイプは file() ドライバーとよく似ていますが、多少の違いがあり、例えば pipe() は読み書きモードで開くので、/proc/kmsg などの特殊なファイルで使用するのは推奨されません" (この議論については ここの投稿 で読むことができます)。

リモートサーバーからデータを読み取るためにポートを開けるには、以下の構文の source を定義します。UDP の場合:

source s_net { udp(); };

もしくは TCP によってログメッセージを受信するには:

source s_net { tcp(); };

どちらも514番ポートを listen します。

syslog-ng と systemd journal

syslog-ng バージョン 3.6.1 から systemd を使用する Linux 環境ではデフォルトの system() で journald が使われます。

journald と syslog-ng 両方のファイルを使用したい場合、次のように設定を行って下さい。systemd-journald 側は、/etc/systemd/journald.conf ファイルで、Storage=auto に設定するかオプションの設定を削除してください (デフォルトで auto に設定されます)。また、ForwardToSyslog=no に設定するか設定を削除してください (デフォルトで no に設定されます)。/etc/syslog-ng/syslog-ng.conf で、以下の source を使って下さい:

source src {
  system();
  internal();
};

また、syslog-ng のテキストログは維持しつつ journald のログを残したくない場合、/etc/systemd/journald.confStorage=volatileForwardToSyslog=yes を設定してください。この設定を行うと journald はメモリに保存されます。syslog-ng 3.6.3 現在、syslog-ng は journald を system(); ソースとして使用するため Storage=none と設定してしまうと、systemd ジャーナルは全てのメッセージを消去してしまい syslog-ng にメッセージが転送されません。

変更を行った後は systemd-journald.servicesyslog-ng.service デーモンを再起動してください。

Destination

syslog-ng では、ログメッセージはファイルに送信されます。構文は source とよく似ています:

destination <identifier> {destination-driver(params); destination-driver(params); ... };

通常はファイルにログを出力することになりますが、他にも様々な destination-driver にログを書き出すことが可能です: パイプ, Unix ソケット, TCP-UDP ポート, ターミナルまたは特定のプログラムなど。したがって、以下の設定は authlog メッセージを /var/log/auth.log に送信します:

destination authlog { file("/var/log/auth.log"); };

(ユーザーがログインしているとき) usertty() は指定されたユーザーのターミナルにメッセージを送信します。root のターミナルにコンソールメッセージを送りたい場合:

destination console { usertty("root"); };

pipe() を使えばメッセージをパイプに送信することができます。以下の設定は xconsole メッセージを /dev/xconsole パイプに送信します。

destination xconsole { pipe("/dev/xconsole"); };

ネットワーク上にメッセージを送信するには、udp() を使います。以下の destination はログデータを別のサーバーに送信します。

destination remote_server { udp("10.0.0.2" port(514)); };

メッセージのフィルターを作成

filter ステートメントの構文は:

filter <identifier> { expression; };

expression には関数を使うことができます。例えばファシリティコードによってメッセージを選り分ける facility() 関数などです。Linux カーネルには少数ながらログに使うことが出来る facility がいくつかあります。それぞれの facility にはログレベルが存在します。debug は最も情報量が多く、panic は深刻なエラーのみを表示します。facility, ログレベル, プライオリティの名前は /usr/include/sys/syslog.h で確かめることができます。May 11 23:42:31 mimosinnet su(pam_unix)[18569]: session opened for user root by (uid=1000) のような authorization からのメッセージを取り出すには、以下を使います:

filter f_auth { facility(auth); };

facility には論理演算子 and, or, not を使うことが可能です。例えば以下のフィルターは authorization, network news, mail からではないメッセージを選択します:

filter f_debug { not facility(auth, authpriv, news, mail); };

level() 関数はプライオリティレベルによってメッセージを選択します。informational レベルを選択したい場合は:

filter f_info { level(info); };

関数と論理演算子は組み合わせて複雑な条件を作成することができます。以下のフィルターはプライオリティレベルが informational から warning の間で facility が auth, authpriv, mail, news ではないメッセージを抜き出します:

filter f_messages { level(info..warn) and not facility(auth, authpriv, mail, news); };

match("regex" value("TEMPLATE")) 関数を使って正規表現にメッセージをマッチさせて選択することも可能です。例えば:

filter f_failed { match("failed" value("MESSAGE")); };

以下はテンプレートのリストです:

 "AMPM", "BSDTAG", "DATE, C_DATE, R_DATE, S_DATE", "DAY, C_DAY, R_DAY, S_DAY", "FACILITY", "FACILITY_NUM", "FULLDATE, C_FULLDATE, R_FULLDATE, S_FULLDATE", "FULLHOST", "FULLHOST_FROM", "HOUR, C_HOUR, R_HOUR, S_HOUR", "HOUR12, C_HOUR12, R_HOUR12, S_HOUR12", "HOST", "HOST_FROM", "ISODATE, C_ISODATE, R_ISODATE, S_ISODATE", "LEVEL_NUM", "LOGHOST", "MIN, C_MIN, R_MIN, S_MIN", "MONTH, C_MONTH, R_MONTH, S_MONTH", "MONTH_ABBREV, C_MONTH_ABBREV, R_MONTH_ABBREV, S_MONTH_ABBREV", "MONTH_NAME, C_MONTH_NAME, R_MONTH_NAME, S_MONTH_NAME", "MONTH_WEEK, C_MONTH_WEEK, R_MONTH_WEEK, S_MONTH_WEEK", "MSEC, C_MSEC, R_MSEC, S_MSEC", "MSG or MESSAGE", "MSGHDR", "MSGID", "MSGONLY", "PID", "PRI", "PRIORITY or LEVEL", "PROGRAM", "SDATA, .SDATA.SDID.SDNAME", "SEC, C_SEC, R_SEC, S_SEC", "SOURCEIP", "SEQNUM", "STAMP, R_STAMP, S_STAMP", "SYSUPTIME", "TAG", "TAGS", "TZ, C_TZ, R_TZ, S_TZ", "TZOFFSET, C_TZOFFSET, R_TZOFFSET, S_TZOFFSET", "UNIXTIME, C_UNIXTIME, R_UNIXTIME, S_UNIXTIME", "USEC, C_USEC, R_USEC, S_USEC", "YEAR, C_YEAR, R_YEAR, S_YEAR", "WEEK, C_WEEK, R_WEEK, S_WEEK", "WEEK_ABBREV, C_WEEK_ABBREV, R_WEEK_ABBREV, S_WEEK_ABBREV", "WEEK_DAY, C_WEEK_DAY, R_WEEK_DAY, S_WEEK_DAY", "WEEKDAY, C_WEEKDAY, R_WEEKDAY, S_WEEKDAY", "WEEK_DAY_NAME, C_WEEK_DAY_NAME, R_WEEK_DAY_NAME, S_WEEK_DAY_NAME".

特定のリモートホストから受信したメッセージをフィルタリングする場合は、host() 関数を使って下さい:

filter f_host { host( "192.168.1.1" ); };

ログのパス

syslog-ng は source, filter, destination を log ステートメントで接続します。構文は:

log {source(s1); source(s2); ...
filter(f1); filter(f2); ...
destination(d1); destination(d2); ...
flags(flag1[, flag2...]); };

以下は src source からのメッセージを f_info filter でフィルターにかけて mailinfo destination に送信する例です:

log { source(src); filter(f_mail); filter(f_info); destination(mailinfo); };

ヒントとテクニック‎

syslog-ng の背後にあるロジックを理解したら、様々な設定、複雑な設定をすることが可能になります。以下はほんの一例です。

syslog-ng に設定ファイルをリロードさせる

syslog-ng に設定ファイルを再評価させることが可能です。プロセスに SIGHUP を手動で送信するか、systemctl の reload 関数を呼び出して下さい:

# systemctl reload syslog-ng

ログ出力をリモートホストにフェイルオーバー

以下の設定は標準ポート (514) と代替ポートを使って、TCP と UDP プロトコル両方でデフォルトの暗号化されていない syslog パケットを送信します。同一の出力を同一のマシンに4回試行して、確実にパケットを送信します。再起動できないリモートサーバーをデバッグするときに特に便利です。様々なポートとプロトコルを使うことで、ファイアウォールフィルターなどのネットワークの問題を通過できるようにしています。また、ポートフォワーディングやトンネルを使う場合にも有用です。このような設定は、逆接続で開始される ssh 接続のトンネルにうってつけでしょう。

#sending to a remote syslog server on TCP and UDP ports (not encrypted)
destination askapache_failover_loghost {
    tcp("208.86.158.195" port(25214));
    udp("208.86.158.195" port(25214));
    udp("mysyslog1.dyndns.org" port(514));
};
log { 
    source(src); 
    destination(askapache_failover_loghost);
};

そして loghost 側ではログを受信します:

#a USB redirected console for flexible viewing
destination debugging_console {
    file("/dev/ttyU1");
};

# listens on IP addresses and ports, sets the incoming settings
source prone_to_failover_host {
    tcp(ip(208.86.158.195),port(25214));
    udp(ip(208.86.158.195) port(25214));

    udp(default-facility(syslog) default-priority(emerg));
    tcp(default-facility(syslog) default-priority(emerg));
}

# log it
log {
    source(prone_to_failover_host); 
    destination(debugging_console);
};

ログを別のファイルに移動

ログを /var/log/messages から他のファイルに移動するには:

#sshd configuration
destination ssh { file("/var/log/ssh.log"); };
filter f_ssh { program("sshd"); };
log { source(src); filter(f_ssh); destination(ssh); };

loghost として設定

システムを loghost として設定することはとても簡単です。設定に以下のように記述を行い、必要なディレクトリを作成してください。このシンプルな設定では、ログのファイル名はリモートホストの FQDN に基づいて名付けられ、/var/log/remote/ にファイルが配置されます。remote ディレクトリを作成した後、syslog-ng の設定をリロードしてください。

source net { udp(); };
destination remote { file("/var/log/remote/${FULLHOST}-log"); };
log { source(net); destination(remote); };

パフォーマンスの向上

いくつかの方法を使って syslog-ng のパフォーマンスを上げることができます:

ときどき書き込ませる

sync(X) オプションは現在 flush_lines(X) と呼ばれるようになっており、これによってファイルへの書き込みを X 行にバッファします。デフォルトは0です (バッファしません)。

処理やディスク領域の重複を避ける

一つのログメッセージが別々のログファイルに複数回送信されるということがあります。例えば、初期設定では、以下のような定義があります:

destination cron { file("/var/log/cron.log"); };
destination messages { file("/var/log/messages"); };
filter f_cron { facility(cron); };
filter f_messages { level(info..warn) 
       and not facility(auth, authpriv, mail, news); };
log { source(src); filter(f_cron); destination(cron); };
log { source(src); filter(f_messages); destination(messages); };

The same message from the cron facility will end up in both the cron.log and messages files. To change this behavior we can use the final flag, ending up further processing with the message. Therefore, in this example, if we want messages from the cron facility not ending up in the messages file, we should change the cron's log sentence by:

 log { source(src); filter(f_cron); destination(cron); flags(final); };

f_messages フィルターから cron ファシリティを除外する方法もあります:

 filter f_messages { level(info..warn) and not facility(cron, auth, authpriv, mail, news); };

PostgreSQL Destination

このセクションでは2つのロールを使います: sysloglogwriter です。syslogsyslog データベースの管理者であり、logwriter だけが logs テーブルにレコードを追加することができることにします。

ログ用にテーブルを作成する必要はありません。syslog-ng が自動的に作成します。

psql -U postgres
postgres=# CREATE ROLE syslog WITH LOGIN;
postgres=# \password syslog    # Using the \password function is secure because
postgres=# CREATE ROLE logwriter WITH LOGIN;
postgres=# \password logwriter # the password is not saved in history.
postgres=# CREATE DATABASE syslog OWNER syslog;
postgres=# \q # You are done here for the moment

pg_hba.conf を編集して sysloglogwriter が PostgreSQL への接続を確立できるようにします。

/var/lib/postgres/data/pg_hba.conf
# TYPE  DATABASE    USER        CIDR-ADDRESS          METHOD

host    syslog      logwriter   192.168.0.1/24        md5
host    syslog      syslog      192.168.0.10/32       md5

PostgreSQL に設定ファイルをリロードさせてください:

# systemctl reload postgresql

/etc/syslog-ng/syslog-ng.conf を編集して PostgreSQL に書き込む場所と方法を設定します。syslog-ng は logwriter ロールを利用します。

...
#
# SQL logging support
#

destination d_pgsql {
  sql(type(pgsql)
  host("127.0.0.1") username("logwriter") password("password")
  database("syslog")
  table("logs_${HOST}_${R_YEAR}${R_MONTH}${R_DAY}") #or whatever you want, example ${HOST}" for hosts, ${LEVEL}" for levels.. etc
  columns("datetime timestamp with time zone", "host varchar(32)", "program varchar(16)", "pid varchar(16)", "message varchar(200)")
  values("$R_ISODATE", "$HOST", "$PROGRAM", "$PID", "$MSG")
  indexes("datetime", "host", "program", "pid", "message"));
};

log { source(src); destination(d_pgsql); };

最後に、syslog-ng を再起動してください。

# systemctl restart syslog-ng

ちゃんとログが記録されているか確認もしましょう。

psql -U logwriter -d syslog
syslog=> SELECT * FROM <your table name> ORDER BY datetime DESC LIMIT 10;

ISO 8601 タイムスタンプ

ビフォー:

#logger These timestamps are not optimal.
#tail -n 1 /var/log/messages.log
Feb 18 14:25:01 hostname logger: These timestamps are not optimal.
#

ts_format(iso);/etc/syslog-ng/syslog-ng.conf の options セクションに追加。例:

options {
  stats_freq (0);
  flush_lines (0);
  time_reopen (10);
  log_fifo_size (1000);
  long_hostnames(off); 
  use_dns (no);
  use_fqdn (no);
  create_dirs (no);
  keep_hostname (yes);
  perm(0640);
  group("log");
  ts_format(iso);      #make ISO-8601 timestamps
};

そして:

# systemctl reload syslog-ng

アフター:

#logger Now THAT is a timestamp!
#tail -n 2 /var/log/messages.log
Feb 18 14:25:01 hostname logger: These timestamps are not optimal.
2010-02-18T20:23:58-05:00 electron logger: Now THAT is a timestamp!
#

RFC 3339 タイムスタンプ

上記と同じように編集。ただし ts_format には iso の代わりに rfc3339 を使用。

ログレベル

Log levels are defined separately for each logged facility in syslog-ng config. Available log levels are listed in /usr/include/sys/syslog.h :

define LOG_EMERG       0       /* system is unusable */
define LOG_ALERT       1       /* action must be taken immediately */
define LOG_CRIT        2       /* critical conditions */
define LOG_ERR         3       /* error conditions */
define LOG_WARNING     4       /* warning conditions */
define LOG_NOTICE      5       /* normal but significant condition */
define LOG_INFO        6       /* informational */
define LOG_DEBUG       7       /* debug-level messages */

マクロと変数

Macros can be used in both templates, and in destination file names. Macros of syslog-ng OSE.

以下のコードは macroname=value@ の形式でログを /var/log/test.log に書き出します。

template t_test { template("PROGRAM=$PROGRAM@PID=$PID@BSDTAG=$BSDTAG@TAG=$TAG@TAGS=$TAGS@FACILITY=$FACILITY@FACILITY_NUM=$FACILITY_NUM@LEVEL=$LEVEL@LEVEL_NUM=$LEVEL_NUM@PRI=$PRI@PRIORITY=$PRIORITY@FULLHOST=$FULLHOST@FULLHOST_FROM=$FULLHOST_FROM@HOST=$HOST@HOST_FROM=$HOST_FROM@LOGHOST=$LOGHOST@MSGHDR=$MSGHDR@MSGID=$MSGID@MSGONLY=$MSGONLY@MSG=$MSG@MESSAGE=$MESSAGE@SOURCE=$SOURCE@SOURCEIP=$SOURCEIP@SOURCE_IP=$SOURCE_IP@SEQNUM=$SEQNUM@UNIXTIME=$UNIXTIME@FULLDATE=$FULLDATE@ISODATE=$ISODATE@DATE=$DATE@STAMP=$STAMP@TZ=$TZ@TZOFFSET=$TZOFFSET@SEC=$SEC@MIN=$MIN@HOUR=$HOUR@HOUR12=$HOUR12@DAY=$DAY@WEEK=$WEEK@WEEK_DAY=$WEEK_DAY@WEEK_DAY_ABBREV=$WEEK_DAY_ABBREV@WEEK_DAY_NAME=$WEEK_DAY_NAME@MONTH=$MONTH@MONTH_ABBREV=$MONTH_ABBREV@MONTH_NAME=$MONTH_NAME@MONTH_WEEK=$MONTH_WEEK@YEAR=$YEAR@YEAR_DAY=$YEAR_DAY
\n"); template_escape(no); };

destination d_test { file("/var/log/test.log" template(t_test)); };

log { source(s_local); destination(d_test); flags(final); };

You can create your own value list as below once syslog-ng is restarted with: tail /var/log/test.log|tr "@" "\n"

PROGRAM=kernel
PID=
BSDTAG=4A
TAG=04
TAGS=.source.s_local
FACILITY=kern
FACILITY_NUM=0
LEVEL=warning
LEVEL_NUM=4
PRI=4
PRIORITY=warning
FULLHOST=www.askapache.com
FULLHOST_FROM=www.askapache.com
HOST=www.askapache.com
HOST_FROM=www.askapache.com
LOGHOST=
MSGHDR=kernel: 
MSGID=
MSGONLY=Firewall: *INVALID* IN=eth0 OUT= MAC=00:00 SRC=x.x.x.x DST=198.101.159.98 LEN=40 TOS=0x00 PREC=0x00 TTL=113 ID=7730 DF PROTO=TCP SPT=52369 DPT=80 WINDOW=0 RES=0x00 ACK RST URGP=0 
MSG=Firewall: *INVALID* IN=eth0 OUT= MAC=00:00 SRC=x.x.x.x DST=198.101.159.98 LEN=40 TOS=0x00 PREC=0x00 TTL=113 ID=7730 DF PROTO=TCP SPT=52369 DPT=80 WINDOW=0 RES=0x00 ACK RST URGP=0 
MESSAGE=Firewall: *INVALID* IN=eth0 OUT= MAC=00:00 SRC=x.x.x.x DST=198.101.159.98 LEN=40 TOS=0x00 PREC=0x00 TTL=113 ID=7730 DF PROTO=TCP SPT=52369 DPT=80 WINDOW=0 RES=0x00 ACK RST URGP=0 
SOURCE=s_local
SOURCEIP=127.0.0.1
SOURCE_IP=
UNIXTIME=1369742458
FULLDATE=2013 May 28 08:00:58
ISODATE=2013-05-28T08:00:58-04:00
DATE=May 28 08:00:58
STAMP=2013-05-28T08:00:58-04:00
TZ=-04:00
TZOFFSET=-04:00
SEC=58
MIN=00
HOUR=08
HOUR12=
DAY=28
WEEK=21
WEEK_DAY=3
WEEK_DAY_ABBREV=Tue
WEEK_DAY_NAME=Tuesday
MONTH=05
MONTH_ABBREV=May
MONTH_NAME=May
MONTH_WEEK=4
YEAR=2013
YEAR_DAY=148

参照