1
0
Fork 0
mirror of https://code.sup39.dev/repos/Wqawg synced 2025-01-09 07:06:46 +09:00
vervis/src/Vervis/Ssh.hs

137 lines
4.3 KiB
Haskell
Raw Normal View History

2016-03-06 20:58:48 +09:00
{- This file is part of Vervis.
-
- Written in 2016 by fr33domlover <fr33domlover@riseup.net>.
-
- Copying is an act of love. Please copy, reuse and share.
-
- The author(s) have dedicated all copyright and related and neighboring
- rights to this software to the public domain worldwide. This software is
- distributed without any warranty.
-
- You should have received a copy of the CC0 Public Domain Dedication along
- with this software. If not, see
- <http://creativecommons.org/publicdomain/zero/1.0/>.
-}
module Vervis.Ssh
( runSsh
)
where
import Prelude
import Control.Monad (when)
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Logger
2016-03-06 20:58:48 +09:00
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Reader (ReaderT (runReaderT), ask)
import Data.ByteString.Char8 (ByteString, unpack)
import Data.ByteString.Lazy (fromStrict)
import Data.Foldable (find)
import Data.Text (Text, pack)
2016-03-06 20:58:48 +09:00
import Database.Persist
import Database.Persist.Sql (SqlBackend, ConnectionPool, runSqlPool)
import Network.SSH
import Network.SSH.Channel
import Network.SSH.Crypto
import Network.SSH.Session
import Yesod.Default.Main (LogFunc)
2016-03-06 20:58:48 +09:00
import Vervis.Model
import Vervis.Settings
-- TODO:
-- [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)
2016-03-06 20:58:48 +09:00
type Backend = SqlBackend
type Channel = ChannelT ChannelB
type Session = SessionT SessionB ChannelB
type SshChanDB = ReaderT Backend Channel
type SshSessDB = ReaderT Backend Session
src :: Text
src = "SSH"
2016-03-06 20:58:48 +09:00
runChanDB :: SshChanDB a -> Channel a
runChanDB action = do
pool <- lift . lift $ ask
2016-03-06 20:58:48 +09:00
runSqlPool action pool
runSessDB :: SshSessDB a -> Session a
runSessDB action = do
pool <- lift . lift $ ask
2016-03-06 20:58:48 +09:00
runSqlPool action pool
chanFail :: Bool -> ByteString -> Channel ()
chanFail wantReply msg = do
channelError $ unpack msg
when wantReply channelFail
authorize :: Authorize -> Session Bool
authorize (Password _ _) = return False
authorize (PublicKey name key) = do
mkeys <- runSessDB $ do
mp <- getBy $ UniquePersonLogin $ pack name
case mp of
Nothing -> return Nothing
Just (Entity pid _p) ->
fmap Just $ selectList [SshKeyPerson ==. pid] []
case mkeys of
Nothing -> do
$logInfoS src "Auth failed: Invalid user"
2016-03-06 20:58:48 +09:00
return False
Just keys -> do
let eValue (Entity _ v) = v
matches =
(== key) . blobToKey . fromStrict . sshKeyContent . eValue
case find matches keys of
Nothing -> do
$logInfoS src "Auth failed: No matching key found"
2016-03-06 20:58:48 +09:00
return False
Just match -> do
$logInfoS src "Auth succeeded"
2016-03-06 20:58:48 +09:00
return True
handle :: Bool -> ChannelRequest -> Channel ()
handle wantReply request = do
$logDebugS src $ pack $ show request
2016-03-06 20:58:48 +09:00
chanFail wantReply "I don't execute any commands yet, come back later"
ready :: LogFunc -> IO ()
ready = runLoggingT $ $logInfoS src "SSH server component starting"
2016-03-06 20:58:48 +09:00
mkConfig
:: AppSettings
-> ConnectionPool
-> LogFunc
-> IO (Config SessionB ChannelB)
mkConfig settings pool logFunc = do
2016-03-06 20:58:48 +09:00
keyPair <- keyPairFromFile $ appSshKeyFile settings
return $ Config
{ cSession = SessionConfig
{ scAuthMethods = ["publickey"]
, scAuthorize = authorize
, scKeyPair = keyPair
, scRunBaseMonad =
flip runReaderT pool . flip runLoggingT logFunc
2016-03-06 20:58:48 +09:00
}
, cChannel = ChannelConfig
{ ccRequestHandler = handle
, ccRunBaseMonad =
flip runReaderT pool . flip runLoggingT logFunc
2016-03-06 20:58:48 +09:00
}
, cPort = fromIntegral $ appSshPort settings
, cReadyAction = ready logFunc
2016-03-06 20:58:48 +09:00
}
runSsh :: AppSettings -> ConnectionPool -> LogFunc -> IO ()
runSsh settings pool logFunc = do
config <- mkConfig settings pool logFunc
2016-03-06 20:58:48 +09:00
startConfig config