2014-03-31 03:37:54 +00:00
|
|
|
module Propellor.Property.Tor where
|
2014-03-30 04:38:16 +00:00
|
|
|
|
2014-03-31 03:55:59 +00:00
|
|
|
import Propellor
|
2014-03-31 03:37:54 +00:00
|
|
|
import qualified Propellor.Property.File as File
|
|
|
|
import qualified Propellor.Property.Apt as Apt
|
2014-09-23 17:19:26 +00:00
|
|
|
import qualified Propellor.Property.Service as Service
|
2014-11-11 21:34:08 +00:00
|
|
|
import Utility.FileMode
|
|
|
|
|
|
|
|
import System.Posix.Files
|
2015-01-29 05:04:59 +00:00
|
|
|
import Data.Char
|
2014-11-11 21:34:08 +00:00
|
|
|
|
|
|
|
type HiddenServiceName = String
|
2014-03-30 04:38:16 +00:00
|
|
|
|
2015-01-29 05:04:59 +00:00
|
|
|
type BridgeName = String
|
|
|
|
|
|
|
|
-- | Sets up a tor bridge relay. (Not an exit node.)
|
2015-01-25 02:38:10 +00:00
|
|
|
isBridge :: Property NoInfo
|
2015-01-29 05:04:59 +00:00
|
|
|
isBridge = isBridge' []
|
|
|
|
|
|
|
|
isBridge' :: [String] -> Property NoInfo
|
|
|
|
isBridge' extraconfig = setup
|
|
|
|
`requires` Apt.installed ["tor", "ntp"]
|
2014-03-30 19:53:35 +00:00
|
|
|
`describe` "tor bridge"
|
2014-03-30 04:52:02 +00:00
|
|
|
where
|
2015-01-29 05:04:59 +00:00
|
|
|
setup = mainConfig `File.hasContent` config
|
|
|
|
`onChange` restarted
|
|
|
|
config =
|
2014-03-30 04:52:02 +00:00
|
|
|
[ "SocksPort 0"
|
|
|
|
, "ORPort 443"
|
|
|
|
, "BridgeRelay 1"
|
|
|
|
, "Exitpolicy reject *:*"
|
2015-01-29 05:04:59 +00:00
|
|
|
] ++ extraconfig
|
|
|
|
|
|
|
|
-- | Sets up a tor bridge relay with a known name and private key.
|
|
|
|
--
|
|
|
|
-- This can be moved to a different IP without needing to wait to
|
|
|
|
-- accumulate trust.
|
|
|
|
--
|
|
|
|
-- The isBridge property can be used to start
|
|
|
|
-- and then upgraded to this one later.
|
|
|
|
isNamedBridge :: BridgeName -> Property HasInfo
|
|
|
|
isNamedBridge bn = isBridge' ["Nickname " ++ saneNickname bn]
|
|
|
|
`requires` torPrivKey (Context ("tor bridge " ++ bn))
|
|
|
|
|
|
|
|
torPrivKey :: Context -> Property HasInfo
|
|
|
|
torPrivKey context = f `File.hasPrivContent` context
|
|
|
|
`onChange` File.ownerGroup f user user
|
|
|
|
-- install tor first, so the directory exists with right perms
|
|
|
|
`requires` Apt.installed ["tor"]
|
|
|
|
where
|
|
|
|
f = "/var/lib/tor/keys/secret_id_key"
|
2014-03-30 04:38:16 +00:00
|
|
|
|
2015-01-25 02:38:10 +00:00
|
|
|
hiddenServiceAvailable :: HiddenServiceName -> Int -> Property NoInfo
|
2014-11-11 09:42:29 +00:00
|
|
|
hiddenServiceAvailable hn port = hiddenServiceHostName prop
|
2014-11-10 19:39:15 +00:00
|
|
|
where
|
2014-11-11 09:42:29 +00:00
|
|
|
prop = mainConfig `File.containsLines`
|
2014-11-12 21:58:43 +00:00
|
|
|
[ unwords ["HiddenServiceDir", varLib </> hn]
|
|
|
|
, unwords ["HiddenServicePort", show port, "127.0.0.1:" ++ show port]
|
2014-11-10 20:27:36 +00:00
|
|
|
]
|
|
|
|
`describe` "hidden service available"
|
|
|
|
`onChange` Service.reloaded "tor"
|
2015-01-25 02:38:10 +00:00
|
|
|
hiddenServiceHostName p = adjustPropertySatisfy p $ \satisfy -> do
|
2014-11-10 19:39:15 +00:00
|
|
|
r <- satisfy
|
2014-11-11 10:31:17 +00:00
|
|
|
h <- liftIO $ readFile (varLib </> hn </> "hostname")
|
2014-11-12 21:58:43 +00:00
|
|
|
warningMessage $ unwords ["hidden service hostname:", h]
|
2014-11-10 19:39:15 +00:00
|
|
|
return r
|
|
|
|
|
2015-01-25 02:38:10 +00:00
|
|
|
hiddenService :: HiddenServiceName -> Int -> Property NoInfo
|
2014-11-11 09:42:29 +00:00
|
|
|
hiddenService hn port = mainConfig `File.containsLines`
|
2014-11-13 08:49:13 +00:00
|
|
|
[ unwords ["HiddenServiceDir", varLib </> hn]
|
|
|
|
, unwords ["HiddenServicePort", show port, "127.0.0.1:" ++ show port]
|
2014-11-10 19:39:15 +00:00
|
|
|
]
|
2014-11-13 08:49:13 +00:00
|
|
|
`describe` unwords ["hidden service available:", hn, show port]
|
2014-11-11 09:42:29 +00:00
|
|
|
`onChange` restarted
|
2014-11-10 19:39:15 +00:00
|
|
|
|
2015-01-25 02:38:10 +00:00
|
|
|
hiddenServiceData :: IsContext c => HiddenServiceName -> c -> Property HasInfo
|
2014-11-11 21:34:08 +00:00
|
|
|
hiddenServiceData hn context = combineProperties desc
|
|
|
|
[ installonion "hostname"
|
|
|
|
, installonion "private_key"
|
|
|
|
]
|
|
|
|
where
|
2014-11-12 21:58:43 +00:00
|
|
|
desc = unwords ["hidden service data available in", varLib </> hn]
|
2014-11-11 21:34:08 +00:00
|
|
|
installonion f = withPrivData (PrivFile $ varLib </> hn </> f) context $ \getcontent ->
|
|
|
|
property desc $ getcontent $ install $ varLib </> hn </> f
|
|
|
|
install f content = ifM (liftIO $ doesFileExist f)
|
|
|
|
( noChange
|
|
|
|
, ensureProperties
|
|
|
|
[ property desc $ makeChange $ do
|
|
|
|
createDirectoryIfMissing True (takeDirectory f)
|
|
|
|
writeFileProtected f content
|
|
|
|
, File.mode (takeDirectory f) $ combineModes
|
|
|
|
[ownerReadMode, ownerWriteMode, ownerExecuteMode]
|
|
|
|
, File.ownerGroup (takeDirectory f) user user
|
|
|
|
, File.ownerGroup f user user
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|
2015-01-25 02:38:10 +00:00
|
|
|
restarted :: Property NoInfo
|
2014-09-23 17:19:26 +00:00
|
|
|
restarted = Service.restarted "tor"
|
2014-11-11 09:42:29 +00:00
|
|
|
|
|
|
|
mainConfig :: FilePath
|
|
|
|
mainConfig = "/etc/tor/torrc"
|
|
|
|
|
2014-11-11 10:31:17 +00:00
|
|
|
varLib :: FilePath
|
|
|
|
varLib = "/var/lib/tor"
|
2014-11-11 10:31:46 +00:00
|
|
|
|
|
|
|
varRun :: FilePath
|
|
|
|
varRun = "/var/run/tor"
|
2014-11-11 21:34:08 +00:00
|
|
|
|
|
|
|
user :: UserName
|
|
|
|
user = "debian-tor"
|
2015-01-29 05:04:59 +00:00
|
|
|
|
|
|
|
type NickName = String
|
|
|
|
|
|
|
|
-- | Convert String to a valid tor NickName.
|
|
|
|
saneNickname :: String -> NickName
|
|
|
|
saneNickname s
|
|
|
|
| null n = "unnamed"
|
|
|
|
| otherwise = n
|
|
|
|
where
|
|
|
|
legal c = isNumber c || isAsciiUpper c || isAsciiLower c
|
|
|
|
n = take 19 $ filter legal s
|