2021/03/20 CentOS Stream 20201203
2022/08/17 RockyLinux 8.5





シスログに特定文字列が出力されるとメール送信する

サーバやアプライアンス、ネットワーク機器を運用していると特定のログ条件でメールを送信したくなります。

・L2スイッチ間で異なるVLAN同士のケーブルを誤って接続してしまったことを検知したい
・FWでVPN接続したとき、メールで通知したい
・L3スイッチでOSPFが変化したときに通知したい
・正規表現で一致した条件でメール送信したい




ここではログの文言を検知してメール通知する設定をします。
あとは検出したい具体的な文字列を設定すれば、メール通知が完成します。


・ログに特定の文字列が含まれていたらメールを送信
ログを検知してメールを送信するが、特定条件ではメールを送信しない
ログを正規表現で分岐し、メールを送信する


ログに特定の文字列が含まれていたらメールを送信


まずはネットワークで取り込んだデータをファイルに保存するためSELinuxを無効化します。
# setenforce 0

# vi /etc/sysconfig/selinux

SELINUX=disabled

次に/etc/rsyslog.confを編集します。
ログをネットワーク機器から受け取り、メールを送信する設定するための設定を赤字で示しています。
ここではrsyslogのバージョンは、8.1911となります。
#### MODULES ####

module(load="imuxsock"    # provides support for local system logging (e.g. via logger command)
       SysSock.Use="off") # Turn off message reception via local log socket;
                          # local messages are retrieved through imjournal now.
module(load="imjournal"             # provides access to the systemd journal
       StateFile="imjournal.state") # File to store the position in the journal
#module(load="imklog") # reads kernel messages (the same are read from journald)
#module(load="immark") # provides --MARK-- message capability

# Provides UDP syslog reception
# for parameters see http://www.rsyslog.com/doc/imudp.html
#module(load="imudp") # needs to be done just once
#外部からのsyslog転送を受け入れます
input(type="imudp" port="514")

# Provides TCP syslog reception
# for parameters see http://www.rsyslog.com/doc/imtcp.html
#module(load="imtcp") # needs to be done just once
#input(type="imtcp" port="514")

#### GLOBAL DIRECTIVES ####

# Where to place auxiliary files
global(workDirectory="/var/lib/rsyslog")

# Use default timestamp format
module(load="builtin:omfile" Template="RSYSLOG_TraditionalFileFormat")

# Include all config files in /etc/rsyslog.d/
include(file="/etc/rsyslog.d/*.conf" mode="optional")

#メール送信モジュールをロードします
module(load="ommail")


#### RULES ####

# Log all kernel messages to the console.
# Logging much else clutters up the screen.
#kern.*                                                 /dev/console

(略)

# Save boot messages also to boot.log
local7.*                                                /var/log/boot.log


#メール本文を作成。ログ送信元IPとログ内容を指定。 \r\nは改行
template (name="mailBody1" type="string" string="%fromhost-ip%\r\n%msg%")


#ログに「alarm str」が含まれた場合、メール送信
if $msg contains "alarm str" then {
   action(type="ommail" server="192.168.1.100" port="25"
          mailfrom="syslog@example.net"
          mailto="log-alarm@example.net"
          subject.text="Alarm mail"
          template="mailBody1"
          action.execonlyonceeveryinterval="600")
}
#各パラメータで送信メールサーバ、送信元アドレス、送信先アドレスを指定
#templateに明示的にメール本文を指定したほうが今後メンテしやすい
#一度メールを送信したら600秒はメール送信を抑止。多量メールを抑制


# ### sample forwarding rule ###
#action(type="omfwd"
# An on-disk queue is created for this action. If the remote host is
# down, messages are spooled to disk and sent when it is up again.
#queue.filename="fwdRule1"       # unique name prefix for spool files
(略)

本家によれば、上記のようなコンフィグが紹介されています。templateは分かりやすく追加しました。


またメール件名にクライアント情報を含めるなど、動的な処理をしたければtemplateを使用します。
template (name="mailBody" type="string" string="%fromhost-ip% \r\n msg='%msg%'")
template (name="mailSubject" type="string" string="Alert mail(%fromhost-ip%)")

if $msg contains "alarm str" then {
   action(type="ommail" server="192.168.1.100" port="25"

          mailfrom="syslog@example.net"
          mailto="log-alarm@example.net"
          subject.template="mailSubject"
          action.execonlyonceeveryinterval="600")
}

この場合、件名にログの送信者IPが付加されるので、件名とメールソフトのフィルタを組み合わせることも可能です。


設定後、rsyslogを再起動します。
ログの検知テストをします。loggerコマンドを使用します。
# logger "alarm str"
正しくログが検出されれば、メールが送信されます。



ログを検知してメールを送信するが、特定条件ではメールを送信しない

メール送信前に条件分岐を入れてやれば、送信前に処理を除外できます。
template (name="mailBody" type="string" string="%fromhost-ip% \r\n msg='%msg%'")

#「alarm str」と「test」を含んでいる場合は、ここでログを破棄
if $msg contains "alarm str" and $msg contains "test" then stop

#「alarm str」が192.168.1.254から送信されたものなら、ここでログを破棄
if $msg contains "alarm str" and $fromhost-ip == "192.168.1.254" then stop

if $msg contains "alarm str" then {
   action(type="ommail" server="192.168.1.100" port="25"

          mailfrom="syslog@example.net"
          mailto="log-alarm@example.net"
          subject.text="alarm mail"
          action.execonlyonceeveryinterval="600")
}

従来の「チルダ」による破棄は非推奨となり、stop文字列を使用するようになったようです。
なお、条件式に使用できる文字列は以下のようになっています。
contains
ログに指定文字が「含まれるか」。大文字小文字を区別しない場合は contains_i
==
ログが指定文字と「等しいか」。以前はisequal
startswith
ログが指定文字で「始まっているか」。大文字小文字を区別しない場合は startswith_i
regex
正規表現・廃止されました
ereregex
正規表現・廃止されました
and , or
and、or、not、などは引き続き使用できます



ログを正規表現で分岐し、メールを送信する


時刻によってメールの送信先を変更したりする要件があったのですが、ネット上にあまりrsyslogde正規表現を使用した例が無かったので、検証しました。

以下は
・ログの文字列が、いずれかに一致する場合にログを出力
・ログに含まれる時刻が、業務時間(9:00〜17:59)外の場合はメールを送信

なお、これは検証用で、「ログに時刻が含まれている」場合です。
こんなログがあったわけです。
Aug 19 09:10:15 ユーザシステム システムID 09:10:14 ユーザーデバイスの起動」

赤字がシスログサーバのタイムスタンプで、青字がログに含まれるユーザーの時刻ログです。
この条件文は青字部分を判断してます。
たまたまこういうシステムがあったのです。

template (name="mailBody" type="string" string="%fromhost-ip% \r\n msg='%msg%'")

#「device alarm」か「device failure」のいずれかを含んでいる。
if ($msg contains "device alarm") or ($msg contains "device failure") then
{

    #業務時間内 08:00:00〜17:59:59まで。ログのみ
    :msg, ereregex, "((0[89])|(1[0-7])):[0-5][0-9]:[0-5][0-9]"
    {
      #ログ出力
      action(type="omfile" file="/var/log/remote.log")
    }

    #業務時間外
18:00:00〜07:59:59まで。ログ出力とメール送信
    #ダブルクォーテーションと( )の数に注意

    :msg, ereregex, "((0[0-7])|(1[89])|(2[0-3])):[0-5][0-9]:[0-5][0-9]"
    {
      #ログ出力とメール送信
      action(type="omfile" file="/var/log/remote.log")

      action(type="ommail" server="192.168.1.100" port="25"
          mailfrom="syslog@example.net"
          mailto="admin@example.net"
          subject.text="Alarm mail"
          template="mailBody1"
          )
    }

}

こうした分岐のポイントは

1.複数の条件(A or B)を判断させる場合は、条件式(if 〜 then)のほうが適している。
  プロパティ式(:msg ,contains)では複数条件を指定することが出来ない。
2.正規表現が使いたければ、プロパティ式を使用する。
  現在rsysloバージョンでは、条件式のEXPRESSIONsに正規表現は使用できない
3.プロパティ式ではブロックにelseが使用できない。正規表現で分岐させたい場合は複数回判断する。

上記設定は、説明的な意味で2重に条件判断を書きましたが、本来の要件は「業務時間中」も「業務時間外」もログを取得し、「業務時間外」だけメールを飛ばす、なので実際は以下のように書くことができます。
template (name="mailBody" type="string" string="%fromhost-ip% \r\ nmsg='%msg%'")

#「device alarm」か「device failure」のいずれかを含んでいる
if ($msg contains "device alarm") or ($msg contains "device failure") then
{

    action(type="omfile" file="/var/log/remote.log")

    #業務時間外 18:00:00〜08:59:59まで。
    :msg, ereregex, "((0[0-8])|(1[89])|(2[0-3])):[0-5][0-9]:[0-5][0-9]"
    {
      action(type="ommail" server="127.0.0.1" port="25"
          mailfrom="syslog@example.net"
          mailto="admin@example.net"
          subject.text="Alarm mail"
          template="mailBody1"
          )
    }

}


なお、保存ファイルに動的ファイルを使用したい場合は
template (name="mailBody" type="string" string="%fromhost-ip% \r\n msg='%msg%'")
template (name="mailSubject" type="string" string="Alert mail")


template (name="log1" type="string" string="/var/log/%$year%%$month%%$day%.log")

#「device alarm」か「device failure」のいずれかを含んでいる
if ($msg contains "device alarm") or ($msg contains "device failure") then
{

    action(type="omfile" dynafile="log1")

    #業務時間外 18:00:00〜07:59:59まで。
    :msg, ereregex, "((0[0-7])|(1[89])|(2[0-3])):[0-5][0-9]:[0-5][0-9]"
    {
      action(type="ommail" server="192.168.1.100" port="25"
          mailfrom="syslog@example.net"
          mailto="admin@example.net"
          subject.template="mailSubject"
          template="mailBody1"
          )
    }

}

と、dynafileパラメータを使用します。







prev.gif