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 130e8cc1c..ccd761431 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 246c95e28..23eacb485 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 200930aa7..33bb2264c 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 0d243a57d..f2f38ce18 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 6eb0b0574..5be36d8e6 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 7a70dc1e4..2e8f47972 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 8e402a21d..fccb05eaf 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 dbf4fd775..b2e5666f5 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 4cd85330d..ad2188e8f 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 72211bc01..7134e1fad 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 45686804e..5de58d6cb 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 9280403a5..4e0eb9199 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 6fcb6173e..9c7e5a46d 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 = "",