I have a remote Linux (Debian) box on which most of my development work is done. As recommended by many, one should install fail2ban to slow down
bots attempting ssh brute-forcing. Installing/configuring fail2ban is quite straightforward, but after enabling it it’s unclear to me whether it’s
getting the job done. Therefore, I write one program to count the number of failed ssh attempts to check. The full haskell program is attached in the
end.
Running it gives the following output. It’s clear that fail2ban is effective in blocking those malicious IPs.
{-# LANGUAGE OverloadedStrings #-} importqualified Data.Text as S importqualified Data.Text.IO as S import Data.Time import Data.List import Data.Fixed import Data.Function import Control.Lens import Control.Monad
extract_tuple :: Line -> (Date, IP) extract_tuple line = let date_str = S.unpack . S.unwords . take 3 . S.words $ line date = parseTimeOrError False defaultTimeLocale "%b %e %H:%M:%S" date_str date_hour_precision = date & minute .~ 0 & second .~ 0 ip = (!! 2) . reverse . S.words $ line in (date_hour_precision, ip) where hms :: Lens'UTCTime (Int, Int, Pico) hms = lens getter setter where getter t = letTimeOfDay h m s = t & utctDayTime & timeToTimeOfDay in (h, m, s) setter t (h, m, s) = t{utctDayTime = timeOfDayToTime (TimeOfDay h m s)}
minute :: Lens'UTCTimeInt minute = lens (^.hms._2) setter where setter t v = t & hms ._2 .~ v
second :: Lens'UTCTimePico second = lens (^.hms._3) setter where setter t v = t & hms ._3 .~ v
process contents = do y <- (^.year) <$> getCurrentTime S.lines contents & filter predicate & map extract_tuple & aggregate & reverse & take 20 & reverse -- & take 4 & map (_1 . year .~ y) & mapM_ print where predicate = liftM2 (&&) (S.isInfixOf "sshd") (S.isInfixOf "Invalid")
ymd :: Lens'UTCTime (Integer, Int, Int) ymd = lens getter setter where getter t = t & utctDay & toGregorian setter t (y, m, d) = t{utctDay = fromGregorian y m d}
year :: Lens'UTCTimeInteger year = lens (^.ymd._1) setter where setter t y = let (_, m, d) = t^.ymd in t{utctDay = fromGregorian y m d}
main :: IO () main = do S.readFile file >>= process