2016-11-14 17:26:34 +00:00
|
|
|
{- git-annex command
|
|
|
|
-
|
|
|
|
- Copyright 2016 Joey Hess <id@joeyh.name>
|
|
|
|
-
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
|
|
|
-}
|
|
|
|
|
2016-12-20 21:40:36 +00:00
|
|
|
{-# LANGUAGE CPP #-}
|
|
|
|
|
2016-11-14 17:26:34 +00:00
|
|
|
module Command.EnableTor where
|
|
|
|
|
|
|
|
import Command
|
2016-12-24 16:49:28 +00:00
|
|
|
import qualified Annex
|
2016-11-29 21:30:27 +00:00
|
|
|
import P2P.Address
|
2016-12-30 16:31:17 +00:00
|
|
|
import P2P.Annex
|
2016-11-14 17:26:34 +00:00
|
|
|
import Utility.Tor
|
2016-11-29 21:30:27 +00:00
|
|
|
import Annex.UUID
|
2017-11-14 18:14:10 +00:00
|
|
|
#ifndef mingw32_HOST_OS
|
2016-12-20 21:40:36 +00:00
|
|
|
import Config.Files
|
2017-11-14 18:14:10 +00:00
|
|
|
#endif
|
2016-12-24 16:49:28 +00:00
|
|
|
import P2P.IO
|
|
|
|
import qualified P2P.Protocol as P2P
|
|
|
|
import Utility.ThreadScheduler
|
2018-03-06 19:14:53 +00:00
|
|
|
import RemoteDaemon.Transport.Tor
|
2016-12-20 21:40:36 +00:00
|
|
|
|
2016-12-24 16:49:28 +00:00
|
|
|
import Control.Concurrent.Async
|
|
|
|
import qualified Network.Socket as S
|
2016-12-20 21:40:36 +00:00
|
|
|
#ifndef mingw32_HOST_OS
|
|
|
|
import Utility.Su
|
|
|
|
import System.Posix.User
|
|
|
|
#endif
|
2016-11-14 17:26:34 +00:00
|
|
|
|
|
|
|
cmd :: Command
|
|
|
|
cmd = noCommit $ dontCheck repoExists $
|
2016-11-29 21:30:27 +00:00
|
|
|
command "enable-tor" SectionSetup "enable tor hidden service"
|
|
|
|
"uid" (withParams seek)
|
2016-11-14 17:26:34 +00:00
|
|
|
|
|
|
|
seek :: CmdParams -> CommandSeek
|
|
|
|
seek = withWords start
|
|
|
|
|
2016-12-24 16:49:28 +00:00
|
|
|
-- This runs as root, so avoid making any commits or initializing
|
|
|
|
-- git-annex, or doing other things that create root-owned files.
|
2016-11-29 21:30:27 +00:00
|
|
|
start :: [String] -> CommandStart
|
2016-12-20 21:40:36 +00:00
|
|
|
start os = do
|
2016-12-20 21:46:14 +00:00
|
|
|
uuid <- getUUID
|
|
|
|
when (uuid == NoUUID) $
|
|
|
|
giveup "This can only be run in a git-annex repository."
|
2016-12-20 21:40:36 +00:00
|
|
|
#ifndef mingw32_HOST_OS
|
|
|
|
curruserid <- liftIO getEffectiveUserID
|
|
|
|
if curruserid == 0
|
|
|
|
then case readish =<< headMaybe os of
|
|
|
|
Nothing -> giveup "Need user-id parameter."
|
2016-12-20 21:46:14 +00:00
|
|
|
Just userid -> go uuid userid
|
2016-12-20 21:40:36 +00:00
|
|
|
else do
|
2017-11-28 18:40:26 +00:00
|
|
|
showStart' "enable-tor" Nothing
|
2016-12-20 21:40:36 +00:00
|
|
|
gitannex <- liftIO readProgramFile
|
|
|
|
let ps = [Param (cmdname cmd), Param (show curruserid)]
|
2016-12-28 19:55:54 +00:00
|
|
|
sucommand <- liftIO $ mkSuCommand gitannex ps
|
|
|
|
maybe noop showLongNote
|
|
|
|
(describePasswordPrompt' sucommand)
|
|
|
|
ifM (liftIO $ runSuCommand sucommand)
|
2016-12-24 16:49:28 +00:00
|
|
|
( next $ next checkHiddenService
|
2016-12-20 21:40:36 +00:00
|
|
|
, giveup $ unwords $
|
|
|
|
[ "Failed to run as root:" , gitannex ] ++ toCommand ps
|
|
|
|
)
|
|
|
|
#else
|
2016-12-20 21:46:14 +00:00
|
|
|
go uuid 0
|
2016-12-20 21:40:36 +00:00
|
|
|
#endif
|
|
|
|
where
|
2016-12-20 21:46:14 +00:00
|
|
|
go uuid userid = do
|
2016-11-29 21:30:27 +00:00
|
|
|
(onionaddr, onionport) <- liftIO $
|
2016-12-21 18:31:27 +00:00
|
|
|
addHiddenService torAppName userid (fromUUID uuid)
|
2016-11-29 21:30:27 +00:00
|
|
|
storeP2PAddress $ TorAnnex onionaddr onionport
|
2016-11-14 17:26:34 +00:00
|
|
|
stop
|
2016-12-24 16:49:28 +00:00
|
|
|
|
|
|
|
checkHiddenService :: CommandCleanup
|
|
|
|
checkHiddenService = bracket setup cleanup go
|
|
|
|
where
|
|
|
|
setup = do
|
|
|
|
showLongNote "Tor hidden service is configured. Checking connection to it. This may take a few minutes."
|
|
|
|
startlistener
|
|
|
|
|
|
|
|
cleanup = liftIO . cancel
|
|
|
|
|
|
|
|
go _ = check (150 :: Int) =<< filter istoraddr <$> loadP2PAddresses
|
|
|
|
|
|
|
|
istoraddr (TorAnnex _ _) = True
|
|
|
|
|
|
|
|
check 0 _ = giveup "Still unable to connect to hidden service. It might not yet be usable by others. Please check Tor's logs for details."
|
|
|
|
check _ [] = giveup "Somehow didn't get an onion address."
|
|
|
|
check n addrs@(addr:_) = do
|
|
|
|
g <- Annex.gitRepo
|
|
|
|
-- Connect but don't bother trying to auth,
|
|
|
|
-- we just want to know if the tor circuit works.
|
2017-12-05 19:00:50 +00:00
|
|
|
liftIO (tryNonAsync $ connectPeer g addr) >>= \case
|
2016-12-24 16:49:28 +00:00
|
|
|
Left e -> do
|
|
|
|
warning $ "Unable to connect to hidden service. It may not yet have propigated to the Tor network. (" ++ show e ++ ") Will retry.."
|
|
|
|
liftIO $ threadDelaySeconds (Seconds 2)
|
|
|
|
check (n-1) addrs
|
|
|
|
Right conn -> do
|
|
|
|
liftIO $ closeConnection conn
|
|
|
|
showLongNote "Tor hidden service is working."
|
|
|
|
return True
|
|
|
|
|
|
|
|
-- Unless the remotedaemon is already listening on the hidden
|
|
|
|
-- service's socket, start a listener. This is only run during the
|
|
|
|
-- check, and it refuses all auth attempts.
|
|
|
|
startlistener = do
|
|
|
|
r <- Annex.gitRepo
|
|
|
|
u <- getUUID
|
2016-12-30 16:31:17 +00:00
|
|
|
msock <- torSocketFile
|
|
|
|
case msock of
|
2016-12-24 16:49:28 +00:00
|
|
|
Just sockfile -> ifM (liftIO $ haslistener sockfile)
|
|
|
|
( liftIO $ async $ return ()
|
|
|
|
, liftIO $ async $ runlistener sockfile u r
|
|
|
|
)
|
|
|
|
Nothing -> giveup "Could not find socket file in Tor configuration!"
|
|
|
|
|
|
|
|
runlistener sockfile u r = serveUnixSocket sockfile $ \h -> do
|
|
|
|
let conn = P2PConnection
|
|
|
|
{ connRepo = r
|
|
|
|
, connCheckAuth = const False
|
|
|
|
, connIhdl = h
|
|
|
|
, connOhdl = h
|
|
|
|
}
|
2018-03-12 19:19:40 +00:00
|
|
|
runst <- mkRunState Client
|
|
|
|
void $ runNetProto runst conn $ P2P.serveAuth u
|
2016-12-24 16:49:28 +00:00
|
|
|
hClose h
|
|
|
|
|
|
|
|
haslistener sockfile = catchBoolIO $ do
|
|
|
|
soc <- S.socket S.AF_UNIX S.Stream S.defaultProtocol
|
|
|
|
S.connect soc (S.SockAddrUnix sockfile)
|
|
|
|
S.close soc
|
|
|
|
return True
|