python

PythonでSoftEtherのログ監視

誕生日に浴室乾燥の内部の配線が焼き切れて壊れる、人と会う仕事の時に限って名城線が止まる、研究室と自宅のスマート化を担うAIシステムをテンパって削除するなど散々なスタートを切っています。29歳は波乱っぽいです。今年は絶対グランピングに行きたい。温泉旅館にも行きたい。

前からSoftEther VPNのログ監視をやりたかったんだけど、OSS版SoftEtherはsyslogを吐けないのでログを自分で読ませることにした。構成のイメージとしては、VPNサーバーがWindowsなのでpythonとWindowsのタスクスケジューラで定期的にSoftEtherのログをパースして、必要な情報だけSlackのincoming-webhookでVPNチャンネルに投げる形。

もちろん理想はファイルが更新されたタイミングでログを読み出す形なんだけど、ファイル更新をトリガーに任意のスクリプトを走らせるシステムをさくっと(さくっとが重要)組む方法が思いつかなかったのでとりあえずはこの形で10分に1回スクリプトを走らせることに。

ログをreadlinesで1行ずつ読ませて日付をテキストからdatetimeに変換→現在時刻から10分以内のログ以外は捨てる→さらに必要な内容だけwebhookにPOST

のわずか3段階なのでスクリプト自体はすぐ書けた。めっちゃ簡単。

まずメッセージをSlackに投げる部分はslackwebで。あらかじめwebhookのURLさえ取得しておけば秒速で終わる。

import slackweb
def post_to_slack(l):
    url = 'https://hooks.slack.com/services/<YOUR_WEBHOOK_URL>'
    s = slackweb.Slack(url=url)
    s.notify(text=l)

あとはログを読ませる。ログの場所はWindowsでは

C:¥Program Files¥SoftEther VPN Server¥server_log¥vpn_20200226.log

とか。ログは日によって分けるようにしてあるので、その日のログを開くようにしないといけない。そしてWindowsだろうとSoftEtherのログはUTF-8で吐かれているっぽい。(面倒だけどcodecs使ってわざわざShift-JISで読み込んだら規格外の文字が入ってて読めなかった。sjis滅んで。)

from datetime import datetime
import codecs
import os

def read_log():
  today = datetime.today()

 yyyymmdd = str(today.date().year)+(today.date().month<10)*'0'+str(today.date().month)+str(today.date().day)

  os.chdir("C:\\Program Files\\SoftEther VPN Server\\server_log")
  f = "vpn_"+yyyymmdd+".log"
  
  with codecs.open(f, 'r', 'utf-8') as log:
    log = log.readlines()

  for l in log:
    date = l.split(' ')[0].split('-')
    time = l.split(' ')[1].split('.')[0].split(':')
    then = datetime(int(date[0]),int(date[1]),int(date[2]),int(time[0]),int(time[1]),int(time[2]))
    diff = (today - then).seconds / 60

    if diff > 10:
      continue
    
    if l.find('新しい PPP セッション')>0:
      post_to_slack(l = l)
    if l.find('仮想 HUB への接続を試行しています')>0:
      post_to_slack(l = l)
    if l.find('正しく認証されました')>0:
      post_to_slack(l = l)
    if l.find('統計情報')>0:
      post_to_slack(l = l)

dateとtimeで色々とsplitしてるけど、これはSoftEtherのログの形式が

2020-02-26 22:12:16.679 クライアント (IP アドレス ***, ホスト名 “***”, ポート番号 ***) に対応するコネクション “CID-686” が作成されました。

って感じになっているから。スペースでsplitした上で”-“か”:”でsplitすれば日付と時間の情報は取ってこれる。

あとはメッセージ内に特定のキーワード(新しいPPPセッション、認証周りとか)が入ってたらそのままpost_to_slackに流せばいい。これでおおよそ接続元とか使用アカウントとか利用状況のことは全部わかる。実際Slackに投稿されるとこんな感じ。

あとはこいつをタスクスケジューラに登録する。指定日時に1回走らせる形でトリガーを設定して、あとは10分おき無期限に繰り返すように指定してやればいいだけ。

めっちゃ簡単。

認証が失敗したログまで全部Slackに投げるとチャンネルが埋まるので、その辺は上の情報と合わせてデータベース立ててそっちに全部送る感じにするほうが現実的かな。書いたスクリプトにSQLの接続周りかけば今回の作業の延長線上として実装できるはず。