読者です 読者をやめる 読者になる 読者になる

謎言語使いの徒然

適当に気になった技術や言語を流すブログ。

SSH のアクセスログを洗ってみる

scala 勉強 Server

SSH ポート空けてると、不正にログインしようとするやつはいるもの。
また、アカウントが盗まれて変な所から ssh されることもあり得る。

そんなこんなで、/var/log/auth* なログファイルをパースしてディレクトリに振り分ける処理をざっくり書く。

わざとシーケンシャルに書いてみた ubuntu のログで多分パースできるはず。

import java.io.{PrintWriter, File}
import org.slf4j.LoggerFactory
import scala.io.Source

object AuthLogUtil {

  val sshLogPattern = """^([A-Z][a-z]{2}\s+\d\s\d{2}:\d{2}:\d{2}) ([a-z]+) sshd\[\d+\]: (.+)$""".r
  val acceptPattern = """^Accepted .+? for ([_a-zA-Z0-9\- ]+?) from ([0-9.]+).*""".r
  val invalidPattern = """^Failed .+? for ([_a-zA-Z0-9\- ]+?) from ([0-9.]+).*""".r

  val distDir = "./replaces"
  val sourceDir = "./sources"

  val valid = "valid"
  val invalid = "invalid"

  def makePath(path:String*):String = {
    path.mkString(File.separator)
  }

  def using[T <: {def close()}, R](src:T)(func:T => R):R = {
    try {
      func(src)
    } finally {
      src.close()
    }
  }
}

class AuthLogUtil {
  import AuthLogUtil._

  val log = LoggerFactory.getLogger(getClass)

  def isSSHLog(str:String):Boolean = {
    sshLogPattern.findFirstIn(str) match {
      case Some(_) => true
      case _ => false
    }
  }

  def acceptLog(user:String, ip:String, dateTime:String, host:String) {
    val path = makePath(distDir, valid, user.replaceAll(" ", "_"), ip)
    val dir  = new File(path)

    // ディレクトリなければ作る
    if(!dir.exists()) dir.mkdirs()

    // ファイルに書き込む
    val filePath = path + File.separator + "auth.log"
    using(new PrintWriter(filePath)) { fileWriter =>
      fileWriter.println("%s %s %s(%s)".format(dateTime, host, user, ip))
    }
  }

  def invalidLog(user:String, ip:String, dateTime:String, host:String) {
    val path = makePath(distDir, invalid, user.replaceAll(" ", "_"), ip)
    val dir  = new File(path)

    // ディレクトリなければ作る
    if(!dir.exists()) dir.mkdirs()

    // ファイルに書き込む
    val filePath = path + File.separator + "auth.log"
    using(new PrintWriter(filePath)) { fileWriter =>
      fileWriter.println("%s %s %s(%s)".format(dateTime, host, user, ip))
    }
  }

  def parse(logLine:String) {
    logLine match {
      // sshd っぽいログだけ処理
      case sshLogPattern(dateTime, host, message) => {
        message match {
          // accept 系ログだけ処理
          case acceptPattern(user, ip) => acceptLog(user, ip, dateTime, host)

          // invalid 系ログだけ処理
          case invalidPattern(user, ip) => invalidLog(user, ip, dateTime, host)

          // それ以外のログ
          case other => log.warn("Not a Accept or Invalid : " + logLine)
        }
      }

      // 一致しないものは処理しない
      case _ =>
    }
  }
}

object LogRelocator extends App {

  import AuthLogUtil.{sourceDir, using}
  val log = LoggerFactory.getLogger(getClass)

  // 先ずは対象ファイルを列挙
  val sources  = new File(sourceDir)

  // logLine 処理クラス
  val executer = new AuthLogUtil

  // ファイルを1個づつ処理
  sources.list().foreach(fileName => {
    log.info("Execute file : %s".format(fileName))
    // ファイルをオープン
    using(Source.fromFile(sourceDir + File.separator + fileName)) { source =>
      // 1行づつ処理
      source.getLines().foreach(executer.parse)
    }
  })
}

時間がかかってくれることを期待したのだけれど、自宅サーバ(しかもポート変えてると)不正ログインないなーというか、ファイルサイズが小さくて数百ミリ秒で終わってしまう。
どうすべ。。。。