Implement DB-based SSH authentication

This commit is contained in:
fr33domlover 2016-03-06 11:58:48 +00:00
parent 062fb5539a
commit 8cf0f2502c
7 changed files with 207 additions and 14 deletions

View file

@ -1,16 +1,41 @@
TODO really explain how to install.... the stuff below is outdated and Vervis is still in early development. These instructions may be incomplete
misleading... and/or slightly outdated. At the time of writing, you can get a running Vervis
instance if you follow the steps below.
Install from Hackage: Install Darcs, GHC 7.10.3 and a recent `stack` version.
$ cabal install vervis Download the Vervis repo:
Install from unpacked release tarball or source repo:
$ darcs get http://hub.darcs.net/fr33domlover/vervis
$ cd vervis $ cd vervis
$ cabal install
Just play with it without installing: Download fr33domlover's modified `ssh` package:
$ cabal build $ darcs get http://hub.darcs.net/fr33domlover/ssh
$ cabal repl
Update `stack.yaml` to specify that path in the `packages` section:
$ vim stack.yml
Install PostgreSQL. You'll need the server and the client library development
files.
$ sudo apt-get install postgresql # TODO see exactly which pages are needed
Create a new PostgreSQL user and a new database.
$ #TODO take this from the vervis ticket I wrote...
Update `stack.yaml` to specify correct database connection details.
$ vim stack.yaml
Build.
$ stack build --flag vervis:dev
Run.
$ stack exec vervis
Browse to `http://localhost:3000` and have fun.

View file

@ -31,6 +31,14 @@ Person
UniquePersonIdent ident UniquePersonIdent ident
UniquePersonLogin login UniquePersonLogin login
SshKey
person PersonId
name Text
algo ByteString
content ByteString
UniqueSshKey person name
Group Group
ident SharerId ident SharerId

27
src/Network/SSH/Local.hs Normal file
View file

@ -0,0 +1,27 @@
{- 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 Network.SSH.Local
( supportedKeyAlgos
)
where
import Prelude
import Data.ByteString.Char8 (ByteString, pack)
import Network.SSH
supportedKeyAlgos :: [ByteString]
supportedKeyAlgos = map pack supportedKeyAlgorithms

View file

@ -30,6 +30,7 @@ module Vervis.Application
) )
where where
import Control.Concurrent (forkIO)
import Control.Monad.Logger (liftLoc, runLoggingT) import Control.Monad.Logger (liftLoc, runLoggingT)
import Database.Persist.Postgresql (createPostgresqlPool, pgConnStr, import Database.Persist.Postgresql (createPostgresqlPool, pgConnStr,
pgPoolSize, runSqlPool) pgPoolSize, runSqlPool)
@ -55,6 +56,8 @@ import Vervis.Handler.Person
import Vervis.Handler.Project import Vervis.Handler.Project
import Vervis.Handler.Repo import Vervis.Handler.Repo
import Vervis.Ssh (runSsh)
-- This line actually creates our YesodDispatch instance. It is the second half -- This line actually creates our YesodDispatch instance. It is the second half
-- of the call to mkYesodData which occurs in Foundation.hs. Please see the -- of the call to mkYesodData which occurs in Foundation.hs. Please see the
-- comments there for more details. -- comments there for more details.
@ -156,10 +159,11 @@ appMain :: IO ()
appMain = do appMain = do
-- Get the settings from all relevant sources -- Get the settings from all relevant sources
settings <- loadAppSettingsArgs settings <- loadAppSettingsArgs
-- fall back to compile-time values, set to [] to require values at runtime -- Fall back to compile-time values, set to [] to require values at
-- runtime
[configSettingsYmlValue] [configSettingsYmlValue]
-- allow environment variables to override -- Allow environment variables to override
useEnv useEnv
-- Generate the foundation from the settings -- Generate the foundation from the settings
@ -168,6 +172,9 @@ appMain = do
-- Generate a WAI Application from the foundation -- Generate a WAI Application from the foundation
app <- makeApplication foundation app <- makeApplication foundation
-- [experimental] Run SSH server and pray
forkIO $ runSsh settings (appConnPool foundation)
-- Run the application with Warp -- Run the application with Warp
runSettings (warpSettings foundation) app runSettings (warpSettings foundation) app

120
src/Vervis/Ssh.hs Normal file
View file

@ -0,0 +1,120 @@
{- 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.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 (pack)
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 Vervis.Model
import Vervis.Settings
type ChannelB = ReaderT ConnectionPool IO
type SessionB = ReaderT ConnectionPool IO
type Backend = SqlBackend
type Channel = ChannelT ChannelB
type Session = SessionT SessionB ChannelB
type SshChanDB = ReaderT Backend Channel
type SshSessDB = ReaderT Backend Session
runChanDB :: SshChanDB a -> Channel a
runChanDB action = do
pool <- lift ask
runSqlPool action pool
runSessDB :: SshSessDB a -> Session a
runSessDB action = do
pool <- lift ask
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
liftIO $ putStrLn "[SSH] auth failed: invalid user"
return False
Just keys -> do
let eValue (Entity _ v) = v
matches =
(== key) . blobToKey . fromStrict . sshKeyContent . eValue
case find matches keys of
Nothing -> do
liftIO $
putStrLn "[SSH] auth failed: no matching key found"
return False
Just match -> do
liftIO $ putStrLn "[SSH] auth succeeded"
return True
handle :: Bool -> ChannelRequest -> Channel ()
handle wantReply request = do
liftIO $ print request
chanFail wantReply "I don't execute any commands yet, come back later"
ready :: IO ()
ready = putStrLn "SSH server component running"
mkConfig :: AppSettings -> ConnectionPool -> IO (Config SessionB ChannelB)
mkConfig settings pool = do
keyPair <- keyPairFromFile $ appSshKeyFile settings
return $ Config
{ cSession = SessionConfig
{ scAuthMethods = ["publickey"]
, scAuthorize = authorize
, scKeyPair = keyPair
, scRunBaseMonad = flip runReaderT pool
}
, cChannel = ChannelConfig
{ ccRequestHandler = handle
, ccRunBaseMonad = flip runReaderT pool
}
, cPort = fromIntegral $ appSshPort settings
, cReadyAction = ready
}
runSsh :: AppSettings -> ConnectionPool -> IO ()
runSsh settings pool = do
config <- mkConfig settings pool
startConfig config

View file

@ -8,10 +8,13 @@ resolver: lts-5.1
# Local packages, usually specified by relative directory name # Local packages, usually specified by relative directory name
packages: packages:
- '.' - '.'
- '/home/fr33domlover/Repos/other-work/ssh'
# Packages to be pulled from upstream that are not in the resolver (e.g., # Packages to be pulled from upstream that are not in the resolver (e.g.,
# acme-missiles-0.3) # acme-missiles-0.3)
extra-deps: [] extra-deps:
- SimpleAES-0.4.2
# - ssh-0.3.2
# Override default flag values for local packages and extra-deps # Override default flag values for local packages and extra-deps
flags: {} flags: {}

View file

@ -39,6 +39,7 @@ library
Data.Git.Local Data.Git.Local
Data.Graph.Inductive.Local Data.Graph.Inductive.Local
Data.List.Local Data.List.Local
Network.SSH.Local
Vervis.Application Vervis.Application
Vervis.Field.Person Vervis.Field.Person
Vervis.Field.Project Vervis.Field.Project
@ -60,6 +61,7 @@ library
Vervis.Handler.Repo Vervis.Handler.Repo
Vervis.Handler.Util Vervis.Handler.Util
Vervis.Path Vervis.Path
Vervis.Ssh
Vervis.Style Vervis.Style
-- other-modules: -- other-modules:
default-extensions: TemplateHaskell default-extensions: TemplateHaskell
@ -118,6 +120,7 @@ library
, persistent-template >= 2.0 && < 2.3 , persistent-template >= 2.0 && < 2.3
, safe , safe
, shakespeare >= 2.0 && < 2.1 , shakespeare >= 2.0 && < 2.1
, ssh
, template-haskell , template-haskell
, text >= 0.11 && < 2.0 , text >= 0.11 && < 2.0
, time , time