Implement logging for SSH using monad-logger and fast-logger
This commit is contained in:
parent
20fb5181cd
commit
fc4690324c
3 changed files with 49 additions and 26 deletions
|
@ -47,6 +47,7 @@ import Network.Wai.Middleware.RequestLogger (Destination (Logger),
|
||||||
mkRequestLogger, outputFormat)
|
mkRequestLogger, outputFormat)
|
||||||
import System.Log.FastLogger (defaultBufSize, newStdoutLoggerSet,
|
import System.Log.FastLogger (defaultBufSize, newStdoutLoggerSet,
|
||||||
toLogStr)
|
toLogStr)
|
||||||
|
import Yesod.Default.Main (LogFunc)
|
||||||
|
|
||||||
-- Import all relevant handler modules here.
|
-- Import all relevant handler modules here.
|
||||||
-- Don't forget to add new modules to your cabal file!
|
-- Don't forget to add new modules to your cabal file!
|
||||||
|
@ -64,6 +65,9 @@ import Vervis.Ssh (runSsh)
|
||||||
-- comments there for more details.
|
-- comments there for more details.
|
||||||
mkYesodDispatch "App" resourcesApp
|
mkYesodDispatch "App" resourcesApp
|
||||||
|
|
||||||
|
loggingFunction :: App -> LogFunc
|
||||||
|
loggingFunction app = messageLoggerSource app (appLogger app)
|
||||||
|
|
||||||
-- | This function allocates resources (such as a database connection pool),
|
-- | This function allocates resources (such as a database connection pool),
|
||||||
-- performs initialization and returns a foundation datatype value. This is also
|
-- performs initialization and returns a foundation datatype value. This is also
|
||||||
-- the place to put your migrate statements to have automatic database
|
-- the place to put your migrate statements to have automatic database
|
||||||
|
@ -88,7 +92,7 @@ makeFoundation appSettings = do
|
||||||
-- information, see:
|
-- information, see:
|
||||||
-- https://ocharles.org.uk/blog/posts/2014-12-04-record-wildcards.html
|
-- https://ocharles.org.uk/blog/posts/2014-12-04-record-wildcards.html
|
||||||
tempFoundation = mkFoundation $ error "connPool forced in tempFoundation"
|
tempFoundation = mkFoundation $ error "connPool forced in tempFoundation"
|
||||||
logFunc = messageLoggerSource tempFoundation appLogger
|
logFunc = loggingFunction tempFoundation
|
||||||
|
|
||||||
-- Create the database connection pool
|
-- Create the database connection pool
|
||||||
pool <- flip runLoggingT logFunc $ createPostgresqlPool
|
pool <- flip runLoggingT logFunc $ createPostgresqlPool
|
||||||
|
@ -130,9 +134,8 @@ warpSettings foundation =
|
||||||
setPort (appPort $ appSettings foundation)
|
setPort (appPort $ appSettings foundation)
|
||||||
$ setHost (appHost $ appSettings foundation)
|
$ setHost (appHost $ appSettings foundation)
|
||||||
$ setOnException (\_req e ->
|
$ setOnException (\_req e ->
|
||||||
when (defaultShouldDisplayException e) $ messageLoggerSource
|
when (defaultShouldDisplayException e) $ loggingFunction
|
||||||
foundation
|
foundation
|
||||||
(appLogger foundation)
|
|
||||||
$(qLocation >>= liftLoc)
|
$(qLocation >>= liftLoc)
|
||||||
"yesod"
|
"yesod"
|
||||||
LevelError
|
LevelError
|
||||||
|
@ -155,6 +158,13 @@ getAppSettings = loadAppSettings [configSettingsYml] [] useEnv
|
||||||
develMain :: IO ()
|
develMain :: IO ()
|
||||||
develMain = develMainHelper getApplicationDev
|
develMain = develMainHelper getApplicationDev
|
||||||
|
|
||||||
|
sshServer :: App -> IO ()
|
||||||
|
sshServer foundation =
|
||||||
|
runSsh
|
||||||
|
(appSettings foundation)
|
||||||
|
(appConnPool foundation)
|
||||||
|
(loggingFunction foundation)
|
||||||
|
|
||||||
-- | The @main@ function for an executable running this site.
|
-- | The @main@ function for an executable running this site.
|
||||||
appMain :: IO ()
|
appMain :: IO ()
|
||||||
appMain = do
|
appMain = do
|
||||||
|
@ -174,7 +184,7 @@ appMain = do
|
||||||
app <- makeApplication foundation
|
app <- makeApplication foundation
|
||||||
|
|
||||||
-- [experimental] Run SSH server and pray
|
-- [experimental] Run SSH server and pray
|
||||||
forkIO $ runSsh settings (appConnPool foundation)
|
forkIO $ sshServer foundation
|
||||||
|
|
||||||
-- Run the application with Warp
|
-- Run the application with Warp
|
||||||
runSettings (warpSettings foundation) app
|
runSettings (warpSettings foundation) app
|
||||||
|
|
|
@ -87,8 +87,5 @@ checkContent =
|
||||||
Left s -> Left $ T.pack s
|
Left s -> Left $ T.pack s
|
||||||
Right b -> Right b
|
Right b -> Right b
|
||||||
|
|
||||||
--TODO make the above work over ByteString and when passes the check, apply
|
|
||||||
--base64 conversion. delete my rel4 key from the DB and re-insert...
|
|
||||||
|
|
||||||
contentField :: Field Handler ByteString
|
contentField :: Field Handler ByteString
|
||||||
contentField = checkContent bsField
|
contentField = checkContent bsField
|
||||||
|
|
|
@ -22,24 +22,32 @@ import Prelude
|
||||||
|
|
||||||
import Control.Monad (when)
|
import Control.Monad (when)
|
||||||
import Control.Monad.IO.Class (liftIO)
|
import Control.Monad.IO.Class (liftIO)
|
||||||
|
import Control.Monad.Logger
|
||||||
import Control.Monad.Trans.Class (lift)
|
import Control.Monad.Trans.Class (lift)
|
||||||
import Control.Monad.Trans.Reader (ReaderT (runReaderT), ask)
|
import Control.Monad.Trans.Reader (ReaderT (runReaderT), ask)
|
||||||
import Data.ByteString.Char8 (ByteString, unpack)
|
import Data.ByteString.Char8 (ByteString, unpack)
|
||||||
import Data.ByteString.Lazy (fromStrict)
|
import Data.ByteString.Lazy (fromStrict)
|
||||||
import Data.Foldable (find)
|
import Data.Foldable (find)
|
||||||
import Data.Text (pack)
|
import Data.Text (Text, pack)
|
||||||
import Database.Persist
|
import Database.Persist
|
||||||
import Database.Persist.Sql (SqlBackend, ConnectionPool, runSqlPool)
|
import Database.Persist.Sql (SqlBackend, ConnectionPool, runSqlPool)
|
||||||
import Network.SSH
|
import Network.SSH
|
||||||
import Network.SSH.Channel
|
import Network.SSH.Channel
|
||||||
import Network.SSH.Crypto
|
import Network.SSH.Crypto
|
||||||
import Network.SSH.Session
|
import Network.SSH.Session
|
||||||
|
import Yesod.Default.Main (LogFunc)
|
||||||
|
|
||||||
import Vervis.Model
|
import Vervis.Model
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
|
|
||||||
type ChannelB = ReaderT ConnectionPool IO
|
-- TODO:
|
||||||
type SessionB = ReaderT ConnectionPool IO
|
-- [x] Implement serious logging (info, warning, error, etc.) with
|
||||||
|
-- monad-logger, maybe see how loggin works in the scaffolding
|
||||||
|
-- [ ] See which git commands darcsden SSH supports and see if I can implement
|
||||||
|
-- them with Hit (i think it was git upload-pack)
|
||||||
|
|
||||||
|
type ChannelB = LoggingT (ReaderT ConnectionPool IO)
|
||||||
|
type SessionB = LoggingT (ReaderT ConnectionPool IO)
|
||||||
type Backend = SqlBackend
|
type Backend = SqlBackend
|
||||||
|
|
||||||
type Channel = ChannelT ChannelB
|
type Channel = ChannelT ChannelB
|
||||||
|
@ -47,14 +55,17 @@ type Session = SessionT SessionB ChannelB
|
||||||
type SshChanDB = ReaderT Backend Channel
|
type SshChanDB = ReaderT Backend Channel
|
||||||
type SshSessDB = ReaderT Backend Session
|
type SshSessDB = ReaderT Backend Session
|
||||||
|
|
||||||
|
src :: Text
|
||||||
|
src = "SSH"
|
||||||
|
|
||||||
runChanDB :: SshChanDB a -> Channel a
|
runChanDB :: SshChanDB a -> Channel a
|
||||||
runChanDB action = do
|
runChanDB action = do
|
||||||
pool <- lift ask
|
pool <- lift . lift $ ask
|
||||||
runSqlPool action pool
|
runSqlPool action pool
|
||||||
|
|
||||||
runSessDB :: SshSessDB a -> Session a
|
runSessDB :: SshSessDB a -> Session a
|
||||||
runSessDB action = do
|
runSessDB action = do
|
||||||
pool <- lift ask
|
pool <- lift . lift $ ask
|
||||||
runSqlPool action pool
|
runSqlPool action pool
|
||||||
|
|
||||||
chanFail :: Bool -> ByteString -> Channel ()
|
chanFail :: Bool -> ByteString -> Channel ()
|
||||||
|
@ -73,7 +84,7 @@ authorize (PublicKey name key) = do
|
||||||
fmap Just $ selectList [SshKeyPerson ==. pid] []
|
fmap Just $ selectList [SshKeyPerson ==. pid] []
|
||||||
case mkeys of
|
case mkeys of
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
liftIO $ putStrLn "[SSH] auth failed: invalid user"
|
$logInfoS src "Auth failed: Invalid user"
|
||||||
return False
|
return False
|
||||||
Just keys -> do
|
Just keys -> do
|
||||||
let eValue (Entity _ v) = v
|
let eValue (Entity _ v) = v
|
||||||
|
@ -81,11 +92,10 @@ authorize (PublicKey name key) = do
|
||||||
(== key) . blobToKey . fromStrict . sshKeyContent . eValue
|
(== key) . blobToKey . fromStrict . sshKeyContent . eValue
|
||||||
case find matches keys of
|
case find matches keys of
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
liftIO $
|
$logInfoS src "Auth failed: No matching key found"
|
||||||
putStrLn "[SSH] auth failed: no matching key found"
|
|
||||||
return False
|
return False
|
||||||
Just match -> do
|
Just match -> do
|
||||||
liftIO $ putStrLn "[SSH] auth succeeded"
|
$logInfoS src "Auth succeeded"
|
||||||
return True
|
return True
|
||||||
|
|
||||||
handle :: Bool -> ChannelRequest -> Channel ()
|
handle :: Bool -> ChannelRequest -> Channel ()
|
||||||
|
@ -93,28 +103,34 @@ handle wantReply request = do
|
||||||
liftIO $ print request
|
liftIO $ print request
|
||||||
chanFail wantReply "I don't execute any commands yet, come back later"
|
chanFail wantReply "I don't execute any commands yet, come back later"
|
||||||
|
|
||||||
ready :: IO ()
|
ready :: LogFunc -> IO ()
|
||||||
ready = putStrLn "SSH server component running"
|
ready = runLoggingT $ $logInfoS src "SSH server component starting"
|
||||||
|
|
||||||
mkConfig :: AppSettings -> ConnectionPool -> IO (Config SessionB ChannelB)
|
mkConfig
|
||||||
mkConfig settings pool = do
|
:: AppSettings
|
||||||
|
-> ConnectionPool
|
||||||
|
-> LogFunc
|
||||||
|
-> IO (Config SessionB ChannelB)
|
||||||
|
mkConfig settings pool logFunc = do
|
||||||
keyPair <- keyPairFromFile $ appSshKeyFile settings
|
keyPair <- keyPairFromFile $ appSshKeyFile settings
|
||||||
return $ Config
|
return $ Config
|
||||||
{ cSession = SessionConfig
|
{ cSession = SessionConfig
|
||||||
{ scAuthMethods = ["publickey"]
|
{ scAuthMethods = ["publickey"]
|
||||||
, scAuthorize = authorize
|
, scAuthorize = authorize
|
||||||
, scKeyPair = keyPair
|
, scKeyPair = keyPair
|
||||||
, scRunBaseMonad = flip runReaderT pool
|
, scRunBaseMonad =
|
||||||
|
flip runReaderT pool . flip runLoggingT logFunc
|
||||||
}
|
}
|
||||||
, cChannel = ChannelConfig
|
, cChannel = ChannelConfig
|
||||||
{ ccRequestHandler = handle
|
{ ccRequestHandler = handle
|
||||||
, ccRunBaseMonad = flip runReaderT pool
|
, ccRunBaseMonad =
|
||||||
|
flip runReaderT pool . flip runLoggingT logFunc
|
||||||
}
|
}
|
||||||
, cPort = fromIntegral $ appSshPort settings
|
, cPort = fromIntegral $ appSshPort settings
|
||||||
, cReadyAction = ready
|
, cReadyAction = ready logFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
runSsh :: AppSettings -> ConnectionPool -> IO ()
|
runSsh :: AppSettings -> ConnectionPool -> LogFunc -> IO ()
|
||||||
runSsh settings pool = do
|
runSsh settings pool logFunc = do
|
||||||
config <- mkConfig settings pool
|
config <- mkConfig settings pool logFunc
|
||||||
startConfig config
|
startConfig config
|
||||||
|
|
Loading…
Reference in a new issue