2012-09-11 01:55:59 +00:00
|
|
|
{- git-annex assistant pairing remote creation
|
|
|
|
-
|
|
|
|
- Copyright 2012 Joey Hess <joey@kitenet.net>
|
|
|
|
-
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
|
|
|
-}
|
|
|
|
|
|
|
|
module Assistant.Pairing.MakeRemote where
|
|
|
|
|
|
|
|
import Assistant.Common
|
|
|
|
import Assistant.Ssh
|
|
|
|
import Assistant.Pairing
|
|
|
|
import Assistant.Pairing.Network
|
|
|
|
import Assistant.MakeRemote
|
|
|
|
|
|
|
|
import Network.Socket
|
|
|
|
import qualified Data.Text as T
|
|
|
|
|
2012-09-11 04:23:34 +00:00
|
|
|
{- Authorized keys are set up before pairing is complete, so that the other
|
|
|
|
- side can immediately begin syncing. -}
|
2012-11-05 16:21:13 +00:00
|
|
|
setupAuthorizedKeys :: PairMsg -> FilePath -> IO ()
|
|
|
|
setupAuthorizedKeys msg repodir = do
|
2012-09-11 04:23:34 +00:00
|
|
|
validateSshPubKey pubkey
|
2012-11-05 16:21:13 +00:00
|
|
|
unlessM (liftIO $ addAuthorizedKeys False repodir pubkey) $
|
2012-09-11 04:23:34 +00:00
|
|
|
error "failed setting up ssh authorized keys"
|
2012-10-31 06:34:03 +00:00
|
|
|
where
|
|
|
|
pubkey = remoteSshPubKey $ pairMsgData msg
|
2012-09-11 04:23:34 +00:00
|
|
|
|
2012-11-05 21:43:17 +00:00
|
|
|
{- When local pairing is complete, this is used to set up the remote for
|
|
|
|
- the host we paired with. -}
|
|
|
|
finishedLocalPairing :: PairMsg -> SshKeyPair -> Assistant ()
|
|
|
|
finishedLocalPairing msg keypair = do
|
2012-10-29 18:07:12 +00:00
|
|
|
sshdata <- liftIO $ setupSshKeyPair keypair =<< pairMsgToSshData msg
|
|
|
|
{- Ensure that we know the ssh host key for the host we paired with.
|
2012-09-11 01:55:59 +00:00
|
|
|
- If we don't, ssh over to get it. -}
|
2012-10-29 18:07:12 +00:00
|
|
|
liftIO $ unlessM (knownHost $ sshHostName sshdata) $
|
2012-09-11 01:55:59 +00:00
|
|
|
void $ sshTranscript
|
|
|
|
[ sshOpt "StrictHostKeyChecking" "no"
|
|
|
|
, sshOpt "NumberOfPasswordPrompts" "0"
|
|
|
|
, "-n"
|
|
|
|
, genSshHost (sshHostName sshdata) (sshUserName sshdata)
|
|
|
|
, "git-annex-shell -c configlist " ++ T.unpack (sshDirectory sshdata)
|
|
|
|
]
|
|
|
|
""
|
2012-10-29 20:22:14 +00:00
|
|
|
void $ makeSshRemote False sshdata
|
2012-09-11 01:55:59 +00:00
|
|
|
|
|
|
|
{- Mostly a straightforward conversion. Except:
|
|
|
|
- * Determine the best hostname to use to contact the host.
|
|
|
|
- * Strip leading ~/ from the directory name.
|
|
|
|
-}
|
|
|
|
pairMsgToSshData :: PairMsg -> IO SshData
|
|
|
|
pairMsgToSshData msg = do
|
|
|
|
let d = pairMsgData msg
|
|
|
|
hostname <- liftIO $ bestHostName msg
|
|
|
|
let dir = case remoteDirectory d of
|
|
|
|
('~':'/':v) -> v
|
|
|
|
v -> v
|
2012-09-13 04:57:52 +00:00
|
|
|
return SshData
|
2012-09-11 01:55:59 +00:00
|
|
|
{ sshHostName = T.pack hostname
|
|
|
|
, sshUserName = Just (T.pack $ remoteUserName d)
|
|
|
|
, sshDirectory = T.pack dir
|
|
|
|
, sshRepoName = genSshRepoName hostname dir
|
2012-12-06 21:09:08 +00:00
|
|
|
, sshPort = 22
|
2012-09-11 01:55:59 +00:00
|
|
|
, needsPubKey = True
|
|
|
|
, rsyncOnly = False
|
2012-09-13 04:57:52 +00:00
|
|
|
}
|
2012-09-11 01:55:59 +00:00
|
|
|
|
|
|
|
{- Finds the best hostname to use for the host that sent the PairMsg.
|
|
|
|
-
|
|
|
|
- If remoteHostName is set, tries to use a .local address based on it.
|
|
|
|
- That's the most robust, if this system supports .local.
|
|
|
|
- Otherwise, looks up the hostname in the DNS for the remoteAddress,
|
|
|
|
- if any. May fall back to remoteAddress if there's no DNS. Ugh. -}
|
|
|
|
bestHostName :: PairMsg -> IO HostName
|
2012-09-13 04:57:52 +00:00
|
|
|
bestHostName msg = case remoteHostName $ pairMsgData msg of
|
2012-09-11 01:55:59 +00:00
|
|
|
Just h -> do
|
|
|
|
let localname = h ++ ".local"
|
2012-09-17 04:18:07 +00:00
|
|
|
addrs <- catchDefaultIO [] $
|
|
|
|
getAddrInfo Nothing (Just localname) Nothing
|
2012-09-11 01:55:59 +00:00
|
|
|
maybe fallback (const $ return localname) (headMaybe addrs)
|
|
|
|
Nothing -> fallback
|
2012-10-31 06:34:03 +00:00
|
|
|
where
|
|
|
|
fallback = do
|
|
|
|
let a = pairMsgAddr msg
|
|
|
|
let sockaddr = case a of
|
|
|
|
IPv4Addr addr -> SockAddrInet (PortNum 0) addr
|
|
|
|
IPv6Addr addr -> SockAddrInet6 (PortNum 0) 0 addr 0
|
|
|
|
fromMaybe (showAddr a)
|
|
|
|
<$> catchDefaultIO Nothing
|
|
|
|
(fst <$> getNameInfo [] True False sockaddr)
|