packages feed

sshd-lint (empty) → 0.1.0.1

raw patch · 7 files changed

+387/−0 lines, 7 filesdep +basedep +containersdep +hspecsetup-changed

Dependencies added: base, containers, hspec, keyword-args, nagios-check, parsec

Files

+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2015 Justin Leitgeb++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ spec/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ spec/fixtures/sshd_config view
@@ -0,0 +1,88 @@+# Package generated configuration file+# See the sshd_config(5) manpage for details++# What ports, IPs and protocols we listen for+Port 22+# Use these options to restrict which interfaces/protocols sshd will bind to+#ListenAddress ::+#ListenAddress 0.0.0.0+Protocol 2+# HostKeys for protocol version 2+HostKey /etc/ssh/ssh_host_rsa_key+HostKey /etc/ssh/ssh_host_dsa_key+HostKey /etc/ssh/ssh_host_ecdsa_key+#Privilege Separation is turned on for security+UsePrivilegeSeparation yes++# Lifetime and size of ephemeral version 1 server key+KeyRegenerationInterval 3600+ServerKeyBits 768++# Logging+SyslogFacility AUTH+LogLevel INFO++# Authentication:+LoginGraceTime 120+PermitRootLogin yes+PermitRootLogin no+StrictModes yes++RSAAuthentication yes+PubkeyAuthentication yes+#AuthorizedKeysFile	%h/.ssh/authorized_keys++# Don't read the user's ~/.rhosts and ~/.shosts files+IgnoreRhosts yes+# For this to work you will also need host keys in /etc/ssh_known_hosts+RhostsRSAAuthentication no+# similar for protocol version 2+HostbasedAuthentication no+# Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication+#IgnoreUserKnownHosts yes++# To enable empty passwords, change to yes (NOT RECOMMENDED)+PermitEmptyPasswords no++# Change to yes to enable challenge-response passwords (beware issues with+# some PAM modules and threads)+ChallengeResponseAuthentication no++# Change to no to disable tunnelled clear text passwords+PasswordAuthentication no++# Kerberos options+#KerberosAuthentication no+#KerberosGetAFSToken no+#KerberosOrLocalPasswd yes+#KerberosTicketCleanup yes++# GSSAPI options+#GSSAPIAuthentication no+#GSSAPICleanupCredentials yes++X11Forwarding yes+X11DisplayOffset 10+PrintMotd no+PrintLastLog yes+TCPKeepAlive yes+#UseLogin no++#MaxStartups 10:30:60+#Banner /etc/issue.net++# Allow client to pass locale environment variables+AcceptEnv LANG LC_*++Subsystem sftp /usr/lib/openssh/sftp-server++# Set this to 'yes' to enable PAM authentication, account processing,+# and session processing. If this is enabled, PAM authentication will+# be allowed through the ChallengeResponseAuthentication and+# PasswordAuthentication.  Depending on your PAM configuration,+# PAM authentication via ChallengeResponseAuthentication may bypass+# the setting of "PermitRootLogin without-password".+# If you just want the PAM account and session checks to run without+# PAM authentication, then enable this but set PasswordAuthentication+# and ChallengeResponseAuthentication to 'no'.+UsePAM yes
+ src/Main.hs view
@@ -0,0 +1,135 @@+{-# LANGUAGE OverloadedStrings #-}++module Main where++import System.Console.GetOpt++import System.Environment (getArgs, getProgName)+import System.Exit (ExitCode(..), exitWith, exitSuccess)++import Paths_sshd_lint (version)++import Data.Version (showVersion)++import Data.KeywordArgs.Parse+import System.SshdLint.Check++import qualified Data.Map as Map+import qualified Data.Set as Set++import Control.Monad (unless, when)+import System.Nagios.Plugin++import Text.Parsec (ParseError, parse)++data Options = Options+    { optShowVersion :: Bool+    , optShowHelp    :: Bool+    , optNagios      :: Bool+    , optFile        :: FilePath+    } deriving Show++defaultOptions :: Options+defaultOptions = Options+    { optShowVersion = False+    , optShowHelp    = False+    , optNagios      = False+    , optFile        = "/etc/ssh/sshd_config"+    }++options :: [OptDescr (Options -> Options)]+options =+  [ Option "V" ["version"] (NoArg (\opts -> opts { optShowVersion = True }))+    "show version number"++  , Option "f" ["file"]+    (ReqArg (\f opts -> opts { optFile = f })+     "FILE") "input FILE"++  , Option "h" ["help"]    (NoArg (\opts -> opts { optShowHelp = True }))+    "show help"++  , Option "n" ["nagios"]    (NoArg (\opts -> opts { optNagios = True }))+    "display output in Nagios-consumable format"+  ]++showHelp :: IO ()+showHelp = do+  prg <- getProgName+  putStrLn (usageInfo prg options)+  exitSuccess++headerMessage :: String -> String+headerMessage progName = "Usage: " ++ progName ++ " [OPTION...]"++lintOpts :: [String] -> IO (Options, [String])+lintOpts argv = do+  name <- getProgName++  case getOpt Permute options argv of+    (o, n, []  ) -> return (foldl (flip id) defaultOptions o, n)+    (_, _, errs) ->+      ioError (userError (concat errs ++ usageInfo (headerMessage name) options))++parseConfig :: String -> Either ParseError [(String, [String])]+parseConfig = parse configParser "(unknown)"++printResult :: String -> [String] -> IO ()+printResult msg errs =+  unless (null errs) $+    do+      name <- getProgName+      putStrLn $ name ++ " encountered the following " ++ msg ++ ":\n"+      mapM_ (\err -> putStrLn $ "\t* " ++ err) errs+      putStr "\n"++runCheck :: Options -> IO ()+runCheck opts = do++  f <- readFile $ optFile opts++  unless (optNagios opts) $ putStrLn ("Checking " ++ optFile opts ++ ":\n")++  case parseConfig f of+    Left e -> putStrLn $ "Parse error: " ++ show e+    Right config -> do+      let dupVals = Set.toList $ duplicatedValues $ map fst config+          recs = recommendations (Map.fromList defaultAcceptedValues) $+                 activeSettings config++      unless (optNagios opts) $ do+        printResult "insecure settings" recs+        printResult "duplicate values" dupVals++      if (not . null) dupVals || (not . null) recs+        then if optNagios opts+             then+               runNagiosPlugin (sshdSafeCheck $ length $ recs ++ dupVals)++             else do+               putStrLn $ show (length (recs ++ dupVals)) ++ " suggestions"+               exitWith (ExitFailure 1)++        else+          putStrLn "No suggestions"++sshdSafeCheck :: Int -> NagiosPlugin ()+sshdSafeCheck errorCount =+  when (errorCount > 0) $+    addResult Critical "Security warnings found in sshd_config"++main :: IO ()+main = do+  opts <- getArgs >>= lintOpts++  name <- getProgName++  if optShowHelp $ fst opts+  then+    putStrLn $ usageInfo (headerMessage name) options+  else+    if optShowVersion $ fst opts then+      putStrLn $ name ++ " " ++ showVersion version +++      " (C) 2015 Stack Builders Inc."+    else+      runCheck $ fst opts
+ src/System/SshdLint/Check.hs view
@@ -0,0 +1,89 @@+module System.SshdLint.Check+       ( duplicatedValues+       , activeSettings+       , recommendations+       , defaultAcceptedValues+       , checkSafeSetting ) where++import Data.Char (toLower)++import qualified Data.Map as Map+import qualified Data.Set as Set++type RecommendedSettings = Map.Map String [String]+type ActiveSettings      = Map.Map String [String]+++defaultAcceptedValues :: [(String, [String])]+defaultAcceptedValues =+  [ ("PermitEmptyPasswords", ["no"])+  , ("PasswordAuthentication", ["no"])+  , ("HostbasedAuthentication", ["no"])+  , ("PermitRootLogin", ["no"])+  , ("IgnoreRhosts", ["yes"])+  , ("Protocol", ["2"])+  , ("StrictModes", ["yes"])+  , ("UsePrivilegeSeparation", ["yes"]) ]++duplicatedValues :: [String] -> Set.Set String+duplicatedValues values =+  snd $ foldr checkDuplicate (Set.empty, Set.empty) values++activeSettings :: [(String, [String])] -> ActiveSettings+activeSettings =+  foldr registerSetting Map.empty++  where registerSetting (configOption, value) =+          Map.insert (map toLower configOption) value++-- | Given a collection of recommendation strings and a current+-- setting, returns a new list of recommendations with new+-- recommendations added depending on the setting of the given+-- configOption and value.+checkSafeSetting :: Map.Map String [String]+                 -> String+                 -> [String]+                 -> [String]+                 -> [String]+checkSafeSetting recs configOption values previousRecommendations =+  let recommendation = Map.lookup configOption recs in++  case recommendation of+    Nothing -> previousRecommendations+    Just r -> if Set.fromList r == Set.fromList values then+                previousRecommendations+              else+                previousRecommendations ++ [ configOption ++ " should be "+                                             ++ show r ++ ", found " +++                                             show values ]+++recommendations :: RecommendedSettings -> ActiveSettings -> [String]+recommendations recommendedSettings settings =+  Map.foldWithKey (checkSafeSetting normalizedRecommendedSettings) []+                  normalizedSettings++  where normalizedRecommendedSettings = lowerCaseMapKeys recommendedSettings+        normalizedSettings = lowerCaseMapKeys settings+++-- | Some values legitimately appear multiple times in the configuration.+allowedDuplicates :: Set.Set String+allowedDuplicates = Set.fromList ["hostkey"]++checkDuplicate :: String+               -> (Set.Set String, Set.Set String)+               -> (Set.Set String, Set.Set String)++checkDuplicate aValue (allValues, dupes) =+  if Set.notMember loweredValue allowedDuplicates &&+     Set.member loweredValue allValues then++    (allValues, Set.insert loweredValue dupes)+  else+    (Set.insert loweredValue allValues, dupes)++  where loweredValue = map toLower aValue++lowerCaseMapKeys :: Map.Map String [String] -> Map.Map String [String]+lowerCaseMapKeys = Map.mapKeys (map toLower)
+ sshd-lint.cabal view
@@ -0,0 +1,52 @@+name:                sshd-lint+version:             0.1.0.1+synopsis:            Check sshd configuration for adherence to best practices++description: If not configured correctly, it may be easy for attackers+             to gain access to a system. sshd-lint checks the sshd_config file+             for adherence to best practices.++license:             MIT+license-file:        LICENSE+author:              Justin Leitgeb+maintainer:          justin@stackbuilders.com+copyright:           2015 Stack Builders Inc.+category:            System+build-type:          Simple+extra-source-files:  spec/fixtures/sshd_config+cabal-version:       >=1.10++executable sshd-lint+  main-is:             Main.hs+  other-modules:       System.SshdLint.Check++  build-depends:         base >=4.5 && <4.8+                       , parsec >= 3.1.0 && <= 3.2+                       , keyword-args >= 0.2.0.1 && < 0.3.0.0+                       , containers+                       , nagios-check++  hs-source-dirs:      src+  default-language:    Haskell2010+  ghc-options:         -Wall++++test-suite sshd-lint-test+  type: exitcode-stdio-1.0+  hs-source-dirs: spec, src+  main-is: Spec.hs+  build-depends:       base >=4.5 && <4.8+                       , parsec >= 3.1.0 && <= 3.2+                       , keyword-args >= 0.2.0.0 && < 0.3.0.0+                       , containers+                       , nagios-check++                       , hspec++  default-language:    Haskell2010+  ghc-options:         -Wall++source-repository head+  type:     git+  location: https://github.com/stackbuilders/sshd-lint