From 4fd13c637c236d5777b6c93e5e38e939940591cf Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sat, 23 Jul 2022 14:49:04 +0100 Subject: [PATCH] core: access messaging servers via SOCKS5 proxy (#835) * core: access messaging servers via SOCKS5 proxy * update option info * update simplexmq --- README.md | 2 +- apps/simplex-chat/Main.hs | 6 ++++- cabal.project | 2 +- docs/CLI.md | 18 ++++++++++++++ package.yaml | 2 ++ scripts/nix/sha256map.nix | 2 +- simplex-chat.cabal | 8 +++++++ src/Simplex/Chat.hs | 12 +++++++--- src/Simplex/Chat/Mobile.hs | 2 ++ src/Simplex/Chat/Options.hs | 46 +++++++++++++++++++++++++++++++++--- src/Simplex/Chat/Terminal.hs | 5 +++- stack.yaml | 2 +- tests/ChatClient.hs | 2 ++ 13 files changed, 97 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 130e8cc1c4..ccd761431b 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ What is already implemented: We plan to add soon: -1. Access to messaging servers via Tor. Currently clients access servers via public Internet, and the servers can observe IP addresses of the clients. Depending which platform you use, you might be able to configure access via Tor independently. The servers provided by SimpleX Chat do not correlate users by or log IP addresses, and you can use your own servers, but in some scenarios that may be not a sufficient level of privacy. +1. Access to messaging servers via Tor. Currently it is supported only for [terminal CLI clients](./docs/CLI.md); mobile clients access servers via public Internet, and the servers can observe IP addresses of the clients. Depending which platform you use, you might be able to configure access via Tor independently. The servers provided by SimpleX Chat do not correlate users by or log IP addresses, and you can use your own servers, but in some scenarios that may be not a sufficient level of privacy. 2. Message queue rotation. Currently the queues created between two users are used until the contact is deleted, providing a long-term pairwise identifiers of the conversation. We are planning to add queue rotation to make these identifiers termporary and rotate based on some schedule TBC (e.g., every X messages, or every X hours/days). 3. Local database encryption. Currently the local chat database stored on your device is not encrypted. 4. Independent implementation audit. diff --git a/apps/simplex-chat/Main.hs b/apps/simplex-chat/Main.hs index 246c95e28f..23eacb4856 100644 --- a/apps/simplex-chat/Main.hs +++ b/apps/simplex-chat/Main.hs @@ -3,6 +3,7 @@ module Main where import Control.Concurrent (threadDelay) +import Network.Socks5 (SocksConf (..)) import Server import Simplex.Chat.Controller (versionNumber) import Simplex.Chat.Core @@ -30,7 +31,10 @@ main = do threadDelay $ chatCmdDelay opts * 1000000 welcome :: ChatOpts -> IO () -welcome ChatOpts {dbFilePrefix} = do +welcome ChatOpts {dbFilePrefix, socksProxy} = do putStrLn $ "SimpleX Chat v" ++ versionNumber putStrLn $ "db: " <> dbFilePrefix <> "_chat.db, " <> dbFilePrefix <> "_agent.db" + case socksProxy of + Just (SocksConf s _) -> putStrLn $ "using SOCKS5 proxy " <> show s + _ -> putStrLn "use -x CLI option to connect via SOCKS5 at :9050" putStrLn "type \"/help\" or \"/h\" for usage info" diff --git a/cabal.project b/cabal.project index 200930aa7c..33bb2264c2 100644 --- a/cabal.project +++ b/cabal.project @@ -5,7 +5,7 @@ constraints: zip +disable-bzip2 +disable-zstd source-repository-package type: git location: https://github.com/simplex-chat/simplexmq.git - tag: eb1f9370c1f254022019906fb6cf741d7687c525 + tag: d788c3ca95f74d7ec2d737f3ef3ad8dc69d32abc source-repository-package type: git diff --git a/docs/CLI.md b/docs/CLI.md index 0d243a57de..f2f38ce180 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -12,6 +12,7 @@ - [Using Haskell stack](#using-haskell-stack) - [Usage](#usage) - [Running the chat client](#running-the-chat-client) + - [Access messaging servers via Tor (BETA)](#access-messaging-servers-via-tor-beta) - [How to use SimpleX chat](#how-to-use-simplex-chat) - [Groups](#groups) - [Sending files](#sending-files) @@ -139,6 +140,23 @@ You can still talk to people using default or any other server - it only affects Run `simplex-chat -h` to see all available options. +### Access messaging servers via Tor (BETA) + +Install Tor and run it as SOCKS5 proxy on port 9050, e.g. on Mac you can: + +``` +brew install tor +brew services start tor +``` + +Use `-x` option to access servers via Tor: + +``` +simplex-chat -x +``` + +You can also use option `--socks-proxy=ipv4:port` or `--socks-proxy=:port` to configure host and port of your SOCKS5 proxy, e.g. if you are running it on some other host or port. + ### How to use SimpleX chat Once you have started the chat, you will be prompted to specify your "display name" and an optional "full name" to create a local chat profile. Your display name is an alias for your contacts to refer to you by - it is not unique and does not serve as a global identity. If some of your contacts chose the same display name, the chat client adds a numeric suffix to their local display name. diff --git a/package.yaml b/package.yaml index 6eb0b0574e..5be36d8e67 100644 --- a/package.yaml +++ b/package.yaml @@ -28,10 +28,12 @@ dependencies: - filepath == 1.4.* - http-types == 0.12.* - mtl == 2.2.* + - network >= 3.1.2.7 && < 3.2 - optparse-applicative >= 0.15 && < 0.17 - process == 1.6.* - simple-logger == 0.1.* - simplexmq >= 3.0 + - socks == 0.6.* - sqlite-simple == 0.4.* - stm == 2.5.* - terminal == 0.2.* diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix index 7a70dc1e4a..2e8f479727 100644 --- a/scripts/nix/sha256map.nix +++ b/scripts/nix/sha256map.nix @@ -1,5 +1,5 @@ { - "https://github.com/simplex-chat/simplexmq.git"."eb1f9370c1f254022019906fb6cf741d7687c525" = "0lwzs3gy2ajjf2iv53ajmm32zix3zi06njgjpr096fv6xhzypbdb"; + "https://github.com/simplex-chat/simplexmq.git"."d788c3ca95f74d7ec2d737f3ef3ad8dc69d32abc" = "0sxaf8by830cc2pfkpn1bkfyr68b9iz901pihip12r9g6v3asssh"; "https://github.com/simplex-chat/aeson.git"."3eb66f9a68f103b5f1489382aad89f5712a64db7" = "0kilkx59fl6c3qy3kjczqvm8c3f4n3p0bdk9biyflf51ljnzp4yp"; "https://github.com/simplex-chat/haskell-terminal.git"."f708b00009b54890172068f168bf98508ffcd495" = "0zmq7lmfsk8m340g47g5963yba7i88n4afa6z93sg9px5jv1mijj"; "https://github.com/zw3rk/android-support.git"."3c3a5ab0b8b137a072c98d3d0937cbdc96918ddb" = "1r6jyxbim3dsvrmakqfyxbd6ms6miaghpbwyl0sr6dzwpgaprz97"; diff --git a/simplex-chat.cabal b/simplex-chat.cabal index 8e402a21d5..fccb05eaf9 100644 --- a/simplex-chat.cabal +++ b/simplex-chat.cabal @@ -76,10 +76,12 @@ library , filepath ==1.4.* , http-types ==0.12.* , mtl ==2.2.* + , network >=3.1.2.7 && <3.2 , optparse-applicative >=0.15 && <0.17 , process ==1.6.* , simple-logger ==0.1.* , simplexmq >=3.0 + , socks ==0.6.* , sqlite-simple ==0.4.* , stm ==2.5.* , terminal ==0.2.* @@ -114,11 +116,13 @@ executable simplex-bot , filepath ==1.4.* , http-types ==0.12.* , mtl ==2.2.* + , network >=3.1.2.7 && <3.2 , optparse-applicative >=0.15 && <0.17 , process ==1.6.* , simple-logger ==0.1.* , simplex-chat , simplexmq >=3.0 + , socks ==0.6.* , sqlite-simple ==0.4.* , stm ==2.5.* , terminal ==0.2.* @@ -153,11 +157,13 @@ executable simplex-bot-advanced , filepath ==1.4.* , http-types ==0.12.* , mtl ==2.2.* + , network >=3.1.2.7 && <3.2 , optparse-applicative >=0.15 && <0.17 , process ==1.6.* , simple-logger ==0.1.* , simplex-chat , simplexmq >=3.0 + , socks ==0.6.* , sqlite-simple ==0.4.* , stm ==2.5.* , terminal ==0.2.* @@ -199,6 +205,7 @@ executable simplex-chat , simple-logger ==0.1.* , simplex-chat , simplexmq >=3.0 + , socks ==0.6.* , sqlite-simple ==0.4.* , stm ==2.5.* , terminal ==0.2.* @@ -249,6 +256,7 @@ test-suite simplex-chat-test , simple-logger ==0.1.* , simplex-chat , simplexmq >=3.0 + , socks ==0.6.* , sqlite-simple ==0.4.* , stm ==2.5.* , terminal ==0.2.* diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index dbf4fd775a..b2e5666f55 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -87,7 +87,13 @@ defaultChatConfig = yesToMigrations = False }, yesToMigrations = False, - defaultServers = InitialAgentServers {smp = _defaultSMPServers, ntf = _defaultNtfServers}, + defaultServers = + InitialAgentServers + { smp = _defaultSMPServers, + ntf = _defaultNtfServers, + socksProxy = Nothing, + tcpTimeout = 5000000 + }, tbqSize = 64, fileChunkSize = 15780, subscriptionConcurrency = 16, @@ -116,7 +122,7 @@ logCfg :: LogConfig logCfg = LogConfig {lc_file = Nothing, lc_stderr = True} newChatController :: SQLiteStore -> Maybe User -> ChatConfig -> ChatOpts -> Maybe (Notification -> IO ()) -> IO ChatController -newChatController chatStore user cfg@ChatConfig {agentConfig = aCfg, tbqSize, defaultServers} ChatOpts {dbFilePrefix, smpServers, logConnections} sendToast = do +newChatController chatStore user cfg@ChatConfig {agentConfig = aCfg, tbqSize, defaultServers} ChatOpts {dbFilePrefix, smpServers, socksProxy, tcpTimeout, logConnections} sendToast = do let f = chatStoreFile dbFilePrefix config = cfg {subscriptionEvents = logConnections} sendNotification = fromMaybe (const $ pure ()) sendToast @@ -124,7 +130,7 @@ newChatController chatStore user cfg@ChatConfig {agentConfig = aCfg, tbqSize, de firstTime <- not <$> doesFileExist f currentUser <- newTVarIO user servers <- resolveServers defaultServers - smpAgent <- getSMPAgentClient aCfg {dbFile = dbFilePrefix <> "_agent.db"} servers + smpAgent <- getSMPAgentClient aCfg {dbFile = dbFilePrefix <> "_agent.db"} servers {socksProxy, tcpTimeout} agentAsync <- newTVarIO Nothing idsDrg <- newTVarIO =<< drgNew inputQ <- newTBQueueIO tbqSize diff --git a/src/Simplex/Chat/Mobile.hs b/src/Simplex/Chat/Mobile.hs index 4cd85330dd..ad2188e8f2 100644 --- a/src/Simplex/Chat/Mobile.hs +++ b/src/Simplex/Chat/Mobile.hs @@ -67,6 +67,8 @@ mobileChatOpts = ChatOpts { dbFilePrefix = undefined, smpServers = [], + socksProxy = Nothing, + tcpTimeout = 5000000, logConnections = False, logAgent = False, chatCmd = "", diff --git a/src/Simplex/Chat/Options.hs b/src/Simplex/Chat/Options.hs index 72211bc018..7134e1fad5 100644 --- a/src/Simplex/Chat/Options.hs +++ b/src/Simplex/Chat/Options.hs @@ -1,6 +1,8 @@ {-# LANGUAGE ApplicativeDo #-} +{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} module Simplex.Chat.Options ( ChatOpts (..), @@ -11,6 +13,9 @@ where import qualified Data.Attoparsec.ByteString.Char8 as A import qualified Data.ByteString.Char8 as B +import Data.Maybe (fromMaybe) +import Network.Socket (HostAddress, SockAddr (..), tupleToHostAddress) +import Network.Socks5 (SocksConf, defaultSocksConf) import Options.Applicative import Simplex.Chat.Controller (updateStr, versionStr) import Simplex.Messaging.Agent.Protocol (SMPServer) @@ -21,6 +26,8 @@ import System.FilePath (combine) data ChatOpts = ChatOpts { dbFilePrefix :: String, smpServers :: [SMPServer], + socksProxy :: Maybe SocksConf, + tcpTimeout :: Int, logConnections :: Bool, logAgent :: Bool, chatCmd :: String, @@ -46,10 +53,26 @@ chatOpts appDir defaultDbFileName = do ( long "server" <> short 's' <> metavar "SERVER" - <> help - "Comma separated list of SMP server(s) to use" + <> help "Comma separated list of SMP server(s) to use" <> value [] ) + socksProxy <- + flag' (Just defaultSocksProxy) (short 'x' <> help "use local SOCKS5 proxy at :9050") + <|> option + parseSocksConf + ( long "socks-proxy" + <> metavar "SOCKS5" + <> help "`ipv4:port` or `:port` of SOCKS5 proxy" + <> value Nothing + ) + t <- + option + auto + ( long "tcp-timeout" + <> metavar "TIMEOUT" + <> help "TCP timeout, seconds (default: 5/10 without/with SOCKS5 proxy)" + <> value 0 + ) logConnections <- switch ( long "connections" @@ -95,13 +118,30 @@ chatOpts appDir defaultDbFileName = do <> short 'm' <> help "Run in maintenance mode (/_start to start chat)" ) - pure ChatOpts {dbFilePrefix, smpServers, logConnections, logAgent, chatCmd, chatCmdDelay, chatServerPort, maintenance} + pure ChatOpts {dbFilePrefix, smpServers, socksProxy, tcpTimeout = useTcpTimeout socksProxy t, logConnections, logAgent, chatCmd, chatCmdDelay, chatServerPort, maintenance} where + useTcpTimeout p t = 1000000 * if t > 0 then t else maybe 5 (const 10) p defaultDbFilePath = combine appDir defaultDbFileName parseSMPServers :: ReadM [SMPServer] parseSMPServers = eitherReader $ parseAll smpServersP . B.pack +defaultSocksHost :: HostAddress +defaultSocksHost = tupleToHostAddress (127, 0, 0, 1) + +defaultSocksProxy :: SocksConf +defaultSocksProxy = defaultSocksConf $ SockAddrInet 9050 defaultSocksHost + +parseSocksConf :: ReadM (Maybe SocksConf) +parseSocksConf = eitherReader $ parseAll socksConfP . B.pack + where + socksConfP = do + host <- maybe defaultSocksHost tupleToHostAddress <$> optional ipv4P + port <- fromMaybe 9050 <$> optional (A.char ':' *> (fromInteger <$> A.decimal)) + pure . Just . defaultSocksConf $ SockAddrInet port host + ipv4P = (,,,) <$> ipNum <*> ipNum <*> ipNum <*> A.decimal + ipNum = A.decimal <* A.char '.' + parseServerPort :: ReadM (Maybe String) parseServerPort = eitherReader $ parseAll serverPortP . B.pack diff --git a/src/Simplex/Chat/Terminal.hs b/src/Simplex/Chat/Terminal.hs index 45686804e9..5de58d6cb6 100644 --- a/src/Simplex/Chat/Terminal.hs +++ b/src/Simplex/Chat/Terminal.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} @@ -28,7 +29,9 @@ terminalChatConfig = "smp://hpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg=@smp5.simplex.im", "smp://PQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo=@smp6.simplex.im" ], - ntf = ["ntf://FB-Uop7RTaZZEG0ZLD2CIaTjsPh-Fw0zFAnb7QyA8Ks=@ntf2.simplex.im"] + ntf = ["ntf://FB-Uop7RTaZZEG0ZLD2CIaTjsPh-Fw0zFAnb7QyA8Ks=@ntf2.simplex.im"], + socksProxy = Nothing, + tcpTimeout = 5000000 } } diff --git a/stack.yaml b/stack.yaml index 9280403a5e..4e0eb9199f 100644 --- a/stack.yaml +++ b/stack.yaml @@ -49,7 +49,7 @@ extra-deps: # - simplexmq-1.0.0@sha256:34b2004728ae396e3ae449cd090ba7410781e2b3cefc59259915f4ca5daa9ea8,8561 # - ../simplexmq - github: simplex-chat/simplexmq - commit: eb1f9370c1f254022019906fb6cf741d7687c525 + commit: d788c3ca95f74d7ec2d737f3ef3ad8dc69d32abc # - terminal-0.2.0.0@sha256:de6770ecaae3197c66ac1f0db5a80cf5a5b1d3b64a66a05b50f442de5ad39570,2977 - github: simplex-chat/aeson commit: 3eb66f9a68f103b5f1489382aad89f5712a64db7 diff --git a/tests/ChatClient.hs b/tests/ChatClient.hs index 6fcb6173ec..9c7e5a46d2 100644 --- a/tests/ChatClient.hs +++ b/tests/ChatClient.hs @@ -49,6 +49,8 @@ testOpts = ChatOpts { dbFilePrefix = undefined, smpServers = ["smp://LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=@localhost:5001"], + socksProxy = Nothing, + tcpTimeout = 5000000, logConnections = False, logAgent = False, chatCmd = "",