adding ssh remote working
Rsync remote still needs work
This commit is contained in:
parent
6623a51cf9
commit
97ce4d24cb
4 changed files with 98 additions and 28 deletions
|
@ -14,6 +14,7 @@ import Assistant.WebApp
|
||||||
import Assistant.WebApp.Types
|
import Assistant.WebApp.Types
|
||||||
import Assistant.WebApp.SideBar
|
import Assistant.WebApp.SideBar
|
||||||
import Utility.Yesod
|
import Utility.Yesod
|
||||||
|
import Assistant.WebApp.Configurators.Local
|
||||||
|
|
||||||
import Yesod
|
import Yesod
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
|
@ -22,6 +23,12 @@ import Network.BSD
|
||||||
import System.Posix.User
|
import System.Posix.User
|
||||||
import System.Process (CreateProcess(..))
|
import System.Process (CreateProcess(..))
|
||||||
|
|
||||||
|
sshConfigurator :: Widget -> Handler RepHtml
|
||||||
|
sshConfigurator a = bootstrap (Just Config) $ do
|
||||||
|
sideBarDisplay
|
||||||
|
setTitle "Add a remote server"
|
||||||
|
a
|
||||||
|
|
||||||
data SshServer = SshServer
|
data SshServer = SshServer
|
||||||
{ hostname :: Maybe Text
|
{ hostname :: Maybe Text
|
||||||
, username :: Maybe Text
|
, username :: Maybe Text
|
||||||
|
@ -66,9 +73,7 @@ usable UsableRsyncServer = True
|
||||||
usable UsableSshServer = True
|
usable UsableSshServer = True
|
||||||
|
|
||||||
getAddSshR :: Handler RepHtml
|
getAddSshR :: Handler RepHtml
|
||||||
getAddSshR = bootstrap (Just Config) $ do
|
getAddSshR = sshConfigurator $ do
|
||||||
sideBarDisplay
|
|
||||||
setTitle "Add a remote server"
|
|
||||||
u <- liftIO $ T.pack . userName
|
u <- liftIO $ T.pack . userName
|
||||||
<$> (getUserEntryForID =<< getEffectiveUserID)
|
<$> (getUserEntryForID =<< getEffectiveUserID)
|
||||||
((result, form), enctype) <- lift $
|
((result, form), enctype) <- lift $
|
||||||
|
@ -82,6 +87,8 @@ getAddSshR = bootstrap (Just Config) $ do
|
||||||
{ sshHostName = fromJust $ hostname sshserver'
|
{ sshHostName = fromJust $ hostname sshserver'
|
||||||
, sshUserName = username sshserver'
|
, sshUserName = username sshserver'
|
||||||
, sshDirectory = fromMaybe "" $ directory sshserver'
|
, sshDirectory = fromMaybe "" $ directory sshserver'
|
||||||
|
-- use unmangled server for repo name
|
||||||
|
, sshRepoName = genSshRepoName sshserver
|
||||||
, pubKey = pubkey
|
, pubKey = pubkey
|
||||||
, rsyncOnly = (status == UsableRsyncServer)
|
, rsyncOnly = (status == UsableRsyncServer)
|
||||||
}
|
}
|
||||||
|
@ -124,18 +131,16 @@ testServer sshserver = do
|
||||||
, checkcommand "git-annex-shell"
|
, checkcommand "git-annex-shell"
|
||||||
, checkcommand "rsync"
|
, checkcommand "rsync"
|
||||||
]
|
]
|
||||||
let user = maybe "" (\u -> T.unpack u ++ "@") $ username s
|
|
||||||
let host = user ++ T.unpack (fromJust $ hostname s)
|
|
||||||
let sshopts = nonempty $ extraopts ++
|
let sshopts = nonempty $ extraopts ++
|
||||||
{- If this is an already known host, let
|
{- If this is an already known host, let
|
||||||
- ssh check it as usual.
|
- ssh check it as usual.
|
||||||
- Otherwise, trust the host key. -}
|
- Otherwise, trust the host key. -}
|
||||||
[ if knownhost then "" else sshopt "StrictHostKeyChecking" "no"
|
[ if knownhost then "" else sshopt "StrictHostKeyChecking" "no"
|
||||||
, "-n" -- don't read from stdin
|
, "-n" -- don't read from stdin
|
||||||
, host
|
, genSshHost (fromJust $ hostname s) (username s)
|
||||||
, remotecommand
|
, remotecommand
|
||||||
]
|
]
|
||||||
parsetranscript <$> sshTranscript sshopts
|
parsetranscript . fst <$> sshTranscript sshopts
|
||||||
parsetranscript s
|
parsetranscript s
|
||||||
| reported "git-annex-shell" = UsableSshServer
|
| reported "git-annex-shell" = UsableSshServer
|
||||||
| reported "rsync" = UsableRsyncServer
|
| reported "rsync" = UsableRsyncServer
|
||||||
|
@ -151,8 +156,27 @@ testServer sshserver = do
|
||||||
report r = "echo " ++ token r
|
report r = "echo " ++ token r
|
||||||
sshopt k v = concat ["-o", k, "=", v]
|
sshopt k v = concat ["-o", k, "=", v]
|
||||||
|
|
||||||
|
{- ssh://user@host/path -}
|
||||||
|
genSshUrl :: SshData -> Text
|
||||||
|
genSshUrl s = T.concat ["ssh://", u, h, d, "/"]
|
||||||
|
where
|
||||||
|
u = maybe "" (\v -> T.concat [v, "@"]) $ sshUserName s
|
||||||
|
h = sshHostName s
|
||||||
|
d
|
||||||
|
| "/" `T.isPrefixOf` sshDirectory s = d
|
||||||
|
| otherwise = T.concat ["/~/", sshDirectory s]
|
||||||
|
|
||||||
|
{- user@host or host -}
|
||||||
|
genSshHost :: Text -> Maybe Text -> String
|
||||||
|
genSshHost host user = maybe "" (\v -> T.unpack v ++ "@") user ++ T.unpack host
|
||||||
|
|
||||||
|
{- host_dir -}
|
||||||
|
genSshRepoName :: SshServer -> String
|
||||||
|
genSshRepoName s = (T.unpack $ fromJust $ hostname s) ++
|
||||||
|
(maybe "" (\d -> '_' : T.unpack d) (directory s))
|
||||||
|
|
||||||
{- The output of ssh, including both stdout and stderr. -}
|
{- The output of ssh, including both stdout and stderr. -}
|
||||||
sshTranscript :: [String] -> IO String
|
sshTranscript :: [String] -> IO (String, Bool)
|
||||||
sshTranscript opts = do
|
sshTranscript opts = do
|
||||||
(readf, writef) <- createPipe
|
(readf, writef) <- createPipe
|
||||||
readh <- fdToHandle readf
|
readh <- fdToHandle readf
|
||||||
|
@ -166,8 +190,8 @@ sshTranscript opts = do
|
||||||
hClose writeh
|
hClose writeh
|
||||||
transcript <- hGetContentsStrict readh
|
transcript <- hGetContentsStrict readh
|
||||||
hClose readh
|
hClose readh
|
||||||
void $ waitForProcess pid
|
ok <- checkSuccessProcess pid
|
||||||
return transcript
|
return (transcript, ok)
|
||||||
|
|
||||||
{- Returns the public key content, and SshServer with a mangled hostname
|
{- Returns the public key content, and SshServer with a mangled hostname
|
||||||
- to use that will enable use of the key. This way we avoid changing the
|
- to use that will enable use of the key. This way we avoid changing the
|
||||||
|
@ -209,29 +233,66 @@ knownHost sshdir (SshServer { hostname = Just h }) =
|
||||||
, return False
|
, return False
|
||||||
)
|
)
|
||||||
|
|
||||||
genSshUrl :: SshData -> Text
|
|
||||||
genSshUrl s = T.concat ["ssh://", u, h, d, "/"]
|
|
||||||
where
|
|
||||||
u = maybe "" (\v -> T.concat [v, "@"]) $ sshUserName s
|
|
||||||
h = sshHostName s
|
|
||||||
d
|
|
||||||
| "/" `T.isPrefixOf` sshDirectory s = d
|
|
||||||
| otherwise = T.concat ["/~/", sshDirectory s]
|
|
||||||
|
|
||||||
getConfirmSshR :: SshData -> Handler RepHtml
|
getConfirmSshR :: SshData -> Handler RepHtml
|
||||||
getConfirmSshR sshdata = bootstrap (Just Config) $ do
|
getConfirmSshR sshdata = sshConfigurator $ do
|
||||||
sideBarDisplay
|
|
||||||
setTitle "Add a remote server"
|
|
||||||
let authtoken = webAppFormAuthToken
|
let authtoken = webAppFormAuthToken
|
||||||
let haspubkey = isJust $ pubKey sshdata
|
let haspubkey = isJust $ pubKey sshdata
|
||||||
$(widgetFile "configurators/confirmssh")
|
$(widgetFile "configurators/confirmssh")
|
||||||
|
|
||||||
|
{- Creates the repository on the remote. Does any necessary ssh key setup.
|
||||||
|
-
|
||||||
|
- This is a one-sided remote setup; the remote server probably does not
|
||||||
|
- have a route to the client here.
|
||||||
|
-}
|
||||||
getMakeSshR :: SshData -> Handler RepHtml
|
getMakeSshR :: SshData -> Handler RepHtml
|
||||||
getMakeSshR sshdata = error "TODO"
|
getMakeSshR sshdata = do
|
||||||
|
(transcript, ok) <- liftIO $ sshTranscript [sshhost, remoteCommand]
|
||||||
|
if ok
|
||||||
|
then do
|
||||||
|
r <- runAnnex undefined $
|
||||||
|
addRemote (sshRepoName sshdata) sshurl
|
||||||
|
syncRemote r
|
||||||
|
redirect RepositoriesR
|
||||||
|
else showerr transcript
|
||||||
where
|
where
|
||||||
makeAuthorizedKeys pubkey = Just $ join ";"
|
sshhost = genSshHost (sshHostName sshdata) (sshUserName sshdata)
|
||||||
|
authline = authorizedKeysLine sshdata
|
||||||
|
remotedir = T.unpack $ sshDirectory sshdata
|
||||||
|
sshurl = T.unpack $ genSshUrl sshdata
|
||||||
|
remoteCommand = join "&&" $ catMaybes
|
||||||
|
[ Just $ "mkdir -p " ++ shellEscape remotedir
|
||||||
|
, Just $ "cd " ++ shellEscape remotedir
|
||||||
|
, Just $ join "&&" makerepo
|
||||||
|
, if null authline
|
||||||
|
then Nothing
|
||||||
|
else Just $ join "&&" makeAuthorizedKeys
|
||||||
|
]
|
||||||
|
makerepo
|
||||||
|
| rsyncOnly sshdata = []
|
||||||
|
| otherwise =
|
||||||
|
[ "git init"
|
||||||
|
, "git annex init"
|
||||||
|
]
|
||||||
|
makeAuthorizedKeys =
|
||||||
[ "mkdir -p ~/.ssh"
|
[ "mkdir -p ~/.ssh"
|
||||||
, "touch ~/.ssh/authorized_keys"
|
, "touch ~/.ssh/authorized_keys"
|
||||||
, "chmod 600 ~/.ssh/authorized_keys"
|
, "chmod 600 ~/.ssh/authorized_keys"
|
||||||
, "echo " ++ shellEscape pubkey ++ " >>~/.ssh/authorized_keys"
|
, unwords
|
||||||
|
[ "echo"
|
||||||
|
, shellEscape authline
|
||||||
|
, ">>~/.ssh/authorized_keys"
|
||||||
|
]
|
||||||
]
|
]
|
||||||
|
showerr msg = sshConfigurator $
|
||||||
|
$(widgetFile "configurators/makessherror")
|
||||||
|
|
||||||
|
authorizedKeysLine :: SshData -> String
|
||||||
|
authorizedKeysLine sshdata@(SshData { pubKey = Just pubkey })
|
||||||
|
{- TODO: Locking down rsync is difficult, requiring a rather
|
||||||
|
- long perl script. -}
|
||||||
|
| rsyncOnly sshdata = pubkey
|
||||||
|
| otherwise = limitcommand "git-annex-shell -c" ++ pubkey
|
||||||
|
where
|
||||||
|
limitcommand c = "command=\"perl -e 'exec qw(" ++ c ++ "), $ENV{SSH_ORIGINAL_COMMAND}'\",no-agent-forwarding,no-port-forwarding,no-X11-forwarding "
|
||||||
|
authorizedKeysLine _ = ""
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,7 @@ data SshData = SshData
|
||||||
{ sshHostName :: Text
|
{ sshHostName :: Text
|
||||||
, sshUserName :: Maybe Text
|
, sshUserName :: Maybe Text
|
||||||
, sshDirectory :: Text
|
, sshDirectory :: Text
|
||||||
|
, sshRepoName :: String
|
||||||
, pubKey :: Maybe PubKey
|
, pubKey :: Maybe PubKey
|
||||||
, rsyncOnly :: Bool
|
, rsyncOnly :: Bool
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
<div .span8>
|
<div .span8>
|
||||||
<p>
|
<p>
|
||||||
The server at #{sshHostName sshdata} has been verified to be usable.
|
The server at #{sshHostName sshdata} has been verified to be usable.
|
||||||
<br>
|
|
||||||
Everything checks out!
|
|
||||||
<p>
|
<p>
|
||||||
<a .btn .btn-primary href="@{MakeSshR sshdata}" onclick="$('#setupmodal').modal('show');">
|
<a .btn .btn-primary href="@{MakeSshR sshdata}" onclick="$('#setupmodal').modal('show');">
|
||||||
Clone this repository to the remote server
|
Clone this repository to the remote server
|
||||||
|
@ -20,7 +18,7 @@
|
||||||
<div .modal .fade #setupmodal>
|
<div .modal .fade #setupmodal>
|
||||||
<div .modal-header>
|
<div .modal-header>
|
||||||
<h3>
|
<h3>
|
||||||
Testing server ...
|
Making repository ...
|
||||||
<div .modal-body>
|
<div .modal-body>
|
||||||
<p>
|
<p>
|
||||||
Setting up repository on the remote server. This could take a minute.
|
Setting up repository on the remote server. This could take a minute.
|
||||||
|
|
10
templates/configurators/makessherror.hamlet
Normal file
10
templates/configurators/makessherror.hamlet
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<div .span9 .hero-unit>
|
||||||
|
<h2>
|
||||||
|
<i .icon-warning-sign></i> Failed to make repository
|
||||||
|
<p>
|
||||||
|
Something went wrong setting up the repository on the remote server.
|
||||||
|
<p>
|
||||||
|
Transcript: #{msg}
|
||||||
|
<p>
|
||||||
|
<a .btn .btn-primary href="@{MakeSshR sshdata}">
|
||||||
|
Retry
|
Loading…
Reference in a new issue