relocate tor socket out of /etc
weasel explained that apparmor limits on what files tor can read do not apply to sockets (because they're not files). And apparently the problems I was seeing with hidden services not being accessible had to do with onion address propigation and not the location of the socket file. remotedaemon looks up the HiddenServicePort in torrc, so if it was previously configured with the socket in /etc, that will still work. This commit was sponsored by Denis Dzyubenko on Patreon.
This commit is contained in:
parent
477aa44209
commit
944a6503b9
4 changed files with 71 additions and 48 deletions
|
@ -19,6 +19,8 @@ git-annex (6.20161211) UNRELEASED; urgency=medium
|
||||||
supporting locales such as, en_GB.UTF-8.
|
supporting locales such as, en_GB.UTF-8.
|
||||||
* rekey --force: Incorrectly marked the new key's content as being
|
* rekey --force: Incorrectly marked the new key's content as being
|
||||||
present in the local repo even when it was not.
|
present in the local repo even when it was not.
|
||||||
|
* enable-tor: Put tor sockets in /var/lib/tor-annex/, rather
|
||||||
|
than in /etc/tor/hidden_service/.
|
||||||
* Fix build with directory-1.3.
|
* Fix build with directory-1.3.
|
||||||
* Debian: Suggest tor and magic-wormhole.
|
* Debian: Suggest tor and magic-wormhole.
|
||||||
* Debian: Build webapp on armel.
|
* Debian: Build webapp on armel.
|
||||||
|
|
|
@ -30,6 +30,6 @@ start ps = case readish =<< headMaybe ps of
|
||||||
when (uuid == NoUUID) $
|
when (uuid == NoUUID) $
|
||||||
giveup "This can only be run in a git-annex repository."
|
giveup "This can only be run in a git-annex repository."
|
||||||
(onionaddr, onionport) <- liftIO $
|
(onionaddr, onionport) <- liftIO $
|
||||||
addHiddenService userid (fromUUID uuid)
|
addHiddenService "tor-annex" userid (fromUUID uuid)
|
||||||
storeP2PAddress $ TorAnnex onionaddr onionport
|
storeP2PAddress $ TorAnnex onionaddr onionport
|
||||||
stop
|
stop
|
||||||
|
|
|
@ -39,36 +39,41 @@ import qualified Network.Socket as S
|
||||||
server :: TransportHandle -> IO ()
|
server :: TransportHandle -> IO ()
|
||||||
server th@(TransportHandle (LocalRepo r) _) = do
|
server th@(TransportHandle (LocalRepo r) _) = do
|
||||||
u <- liftAnnex th getUUID
|
u <- liftAnnex th getUUID
|
||||||
|
|
||||||
q <- newTBMQueueIO maxConnections
|
|
||||||
replicateM_ maxConnections $
|
|
||||||
forkIO $ forever $ serveClient th u r q
|
|
||||||
|
|
||||||
uid <- getRealUserID
|
uid <- getRealUserID
|
||||||
let ident = fromUUID u
|
let ident = fromUUID u
|
||||||
let sock = hiddenServiceSocketFile uid ident
|
go u =<< getHiddenServiceSocketFile uid ident
|
||||||
nukeFile sock
|
where
|
||||||
soc <- S.socket S.AF_UNIX S.Stream S.defaultProtocol
|
go u (Just sock) = do
|
||||||
S.bind soc (S.SockAddrUnix sock)
|
q <- newTBMQueueIO maxConnections
|
||||||
-- Allow everyone to read and write to the socket; tor is probably
|
replicateM_ maxConnections $
|
||||||
-- running as a different user. Connections have to authenticate
|
forkIO $ forever $ serveClient th u r q
|
||||||
-- to do anything, so it's fine that other local users can connect.
|
|
||||||
modifyFileMode sock $ addModes
|
nukeFile sock
|
||||||
[groupReadMode, groupWriteMode, otherReadMode, otherWriteMode]
|
soc <- S.socket S.AF_UNIX S.Stream S.defaultProtocol
|
||||||
S.listen soc 2
|
S.bind soc (S.SockAddrUnix sock)
|
||||||
debugM "remotedaemon" "Tor hidden service running"
|
-- Allow everyone to read and write to the socket; tor
|
||||||
forever $ do
|
-- is probably running as a different user.
|
||||||
(conn, _) <- S.accept soc
|
-- Connections have to authenticate to do anything,
|
||||||
h <- setupHandle conn
|
-- so it's fine that other local users can connect to the
|
||||||
ok <- atomically $ ifM (isFullTBMQueue q)
|
-- socket.
|
||||||
( return False
|
modifyFileMode sock $ addModes
|
||||||
, do
|
[groupReadMode, groupWriteMode, otherReadMode, otherWriteMode]
|
||||||
writeTBMQueue q h
|
|
||||||
return True
|
S.listen soc 2
|
||||||
)
|
debugM "remotedaemon" "Tor hidden service running"
|
||||||
unless ok $ do
|
forever $ do
|
||||||
hClose h
|
(conn, _) <- S.accept soc
|
||||||
warningIO "dropped Tor connection, too busy"
|
h <- setupHandle conn
|
||||||
|
ok <- atomically $ ifM (isFullTBMQueue q)
|
||||||
|
( return False
|
||||||
|
, do
|
||||||
|
writeTBMQueue q h
|
||||||
|
return True
|
||||||
|
)
|
||||||
|
unless ok $ do
|
||||||
|
hClose h
|
||||||
|
warningIO "dropped Tor connection, too busy"
|
||||||
|
go _ Nothing = debugM "remotedaemon" "Tor hidden service not enabled"
|
||||||
|
|
||||||
-- How many clients to serve at a time, maximum. This is to avoid DOS attacks.
|
-- How many clients to serve at a time, maximum. This is to avoid DOS attacks.
|
||||||
maxConnections :: Int
|
maxConnections :: Int
|
||||||
|
|
|
@ -25,8 +25,12 @@ newtype OnionAddress = OnionAddress String
|
||||||
|
|
||||||
type OnionSocket = FilePath
|
type OnionSocket = FilePath
|
||||||
|
|
||||||
|
-- | A unique identifier for a hidden service.
|
||||||
type UniqueIdent = String
|
type UniqueIdent = String
|
||||||
|
|
||||||
|
-- | Name of application that is providing a hidden service.
|
||||||
|
type AppName = String
|
||||||
|
|
||||||
connectHiddenService :: OnionAddress -> OnionPort -> IO Socket
|
connectHiddenService :: OnionAddress -> OnionPort -> IO Socket
|
||||||
connectHiddenService (OnionAddress address) port = do
|
connectHiddenService (OnionAddress address) port = do
|
||||||
(s, _) <- socksConnect torsockconf socksaddr
|
(s, _) <- socksConnect torsockconf socksaddr
|
||||||
|
@ -48,9 +52,9 @@ connectHiddenService (OnionAddress address) port = do
|
||||||
--
|
--
|
||||||
-- If there is already a hidden service for the specified unique
|
-- If there is already a hidden service for the specified unique
|
||||||
-- identifier, returns its information without making any changes.
|
-- identifier, returns its information without making any changes.
|
||||||
addHiddenService :: UserID -> UniqueIdent -> IO (OnionAddress, OnionPort)
|
addHiddenService :: AppName -> UserID -> UniqueIdent -> IO (OnionAddress, OnionPort)
|
||||||
addHiddenService uid ident = do
|
addHiddenService appname uid ident = do
|
||||||
prepHiddenServiceSocketDir uid ident
|
prepHiddenServiceSocketDir appname uid ident
|
||||||
ls <- lines <$> readFile torrc
|
ls <- lines <$> readFile torrc
|
||||||
let portssocks = mapMaybe (parseportsock . separate isSpace) ls
|
let portssocks = mapMaybe (parseportsock . separate isSpace) ls
|
||||||
case filter (\(_, s) -> s == sockfile) portssocks of
|
case filter (\(_, s) -> s == sockfile) portssocks of
|
||||||
|
@ -81,7 +85,7 @@ addHiddenService uid ident = do
|
||||||
return (p, drop 1 (dropWhile (/= ':') l))
|
return (p, drop 1 (dropWhile (/= ':') l))
|
||||||
parseportsock _ = Nothing
|
parseportsock _ = Nothing
|
||||||
|
|
||||||
sockfile = hiddenServiceSocketFile uid ident
|
sockfile = hiddenServiceSocketFile appname uid ident
|
||||||
|
|
||||||
-- An infinite random list of high ports.
|
-- An infinite random list of high ports.
|
||||||
mkhighports g =
|
mkhighports g =
|
||||||
|
@ -104,7 +108,7 @@ addHiddenService uid ident = do
|
||||||
-- The "hs" is used in the name to prevent too long a path name,
|
-- The "hs" is used in the name to prevent too long a path name,
|
||||||
-- which could present problems for socketFile.
|
-- which could present problems for socketFile.
|
||||||
hiddenServiceDir :: UserID -> UniqueIdent -> FilePath
|
hiddenServiceDir :: UserID -> UniqueIdent -> FilePath
|
||||||
hiddenServiceDir uid ident = libDir </> "hs_" ++ show uid ++ "_" ++ ident
|
hiddenServiceDir uid ident = torLibDir </> "hs_" ++ show uid ++ "_" ++ ident
|
||||||
|
|
||||||
hiddenServiceHostnameFile :: UserID -> UniqueIdent -> FilePath
|
hiddenServiceHostnameFile :: UserID -> UniqueIdent -> FilePath
|
||||||
hiddenServiceHostnameFile uid ident = hiddenServiceDir uid ident </> "hostname"
|
hiddenServiceHostnameFile uid ident = hiddenServiceDir uid ident </> "hostname"
|
||||||
|
@ -112,33 +116,45 @@ hiddenServiceHostnameFile uid ident = hiddenServiceDir uid ident </> "hostname"
|
||||||
-- | Location of the socket for a hidden service.
|
-- | Location of the socket for a hidden service.
|
||||||
--
|
--
|
||||||
-- This has to be a location that tor can read from, and that the user
|
-- This has to be a location that tor can read from, and that the user
|
||||||
-- can write to. Tor is often prevented by apparmor from reading
|
-- can write to. Since torLibDir is locked down, it can't go in there.
|
||||||
-- from many locations. Putting it in /etc is a FHS violation, but it's the
|
|
||||||
-- simplest and most robust choice until http://bugs.debian.org/846275 is
|
|
||||||
-- dealt with.
|
|
||||||
--
|
--
|
||||||
-- Note that some unix systems limit socket paths to 92 bytes long.
|
-- Note that some unix systems limit socket paths to 92 bytes long.
|
||||||
-- That should not be a problem if the UniqueIdent is around the length of
|
-- That should not be a problem if the UniqueIdent is around the length of
|
||||||
-- a UUID.
|
-- a UUID, and the AppName is short.
|
||||||
hiddenServiceSocketFile :: UserID -> UniqueIdent -> FilePath
|
hiddenServiceSocketFile :: AppName -> UserID -> UniqueIdent -> FilePath
|
||||||
hiddenServiceSocketFile uid ident = etcDir </> "hidden_services" </> show uid ++ "_" ++ ident </> "s"
|
hiddenServiceSocketFile appname uid ident = varLibDir </> appname </> show uid ++ "_" ++ ident </> "s"
|
||||||
|
|
||||||
|
-- | Parse torrc, to get the socket file used for a hidden service with
|
||||||
|
-- the specified UniqueIdent.
|
||||||
|
getHiddenServiceSocketFile :: UserID -> UniqueIdent -> IO (Maybe FilePath)
|
||||||
|
getHiddenServiceSocketFile uid ident =
|
||||||
|
parse . map words . lines <$> catchDefaultIO "" (readFile torrc)
|
||||||
|
where
|
||||||
|
parse [] = Nothing
|
||||||
|
parse (("HiddenServiceDir":hsdir:[]):("HiddenServicePort":_hsport:hsaddr:[]):rest)
|
||||||
|
| "unix:" `isPrefixOf` hsaddr && hsdir == hsdir_want =
|
||||||
|
Just (drop (length "unix:") hsaddr)
|
||||||
|
| otherwise = parse rest
|
||||||
|
parse (_:rest) = parse rest
|
||||||
|
|
||||||
|
hsdir_want = hiddenServiceDir uid ident
|
||||||
|
|
||||||
-- | Sets up the directory for the socketFile, with appropriate
|
-- | Sets up the directory for the socketFile, with appropriate
|
||||||
-- permissions. Must run as root.
|
-- permissions. Must run as root.
|
||||||
prepHiddenServiceSocketDir :: UserID -> UniqueIdent -> IO ()
|
prepHiddenServiceSocketDir :: AppName -> UserID -> UniqueIdent -> IO ()
|
||||||
prepHiddenServiceSocketDir uid ident = do
|
prepHiddenServiceSocketDir appname uid ident = do
|
||||||
createDirectoryIfMissing True d
|
createDirectoryIfMissing True d
|
||||||
setOwnerAndGroup d uid (-1)
|
setOwnerAndGroup d uid (-1)
|
||||||
modifyFileMode d $
|
modifyFileMode d $
|
||||||
addModes [ownerReadMode, ownerExecuteMode, ownerWriteMode]
|
addModes [ownerReadMode, ownerExecuteMode, ownerWriteMode]
|
||||||
where
|
where
|
||||||
d = takeDirectory $ hiddenServiceSocketFile uid ident
|
d = takeDirectory $ hiddenServiceSocketFile appname uid ident
|
||||||
|
|
||||||
torrc :: FilePath
|
torrc :: FilePath
|
||||||
torrc = "/etc/tor/torrc"
|
torrc = "/etc/tor/torrc"
|
||||||
|
|
||||||
libDir :: FilePath
|
torLibDir :: FilePath
|
||||||
libDir = "/var/lib/tor"
|
torLibDir = "/var/lib/tor"
|
||||||
|
|
||||||
etcDir :: FilePath
|
varLibDir :: FilePath
|
||||||
etcDir = "/etc/tor"
|
varLibDir = "/var/lib"
|
||||||
|
|
Loading…
Reference in a new issue