Browse Source

add support for tor (#369)

tags/v1.1.0-rc1
Shivaram Lingamneni 5 years ago
parent
commit
b0f89062fa
10 changed files with 304 additions and 74 deletions
  1. 44
    0
      docs/MANUAL.md
  2. 59
    46
      irc/client.go
  3. 23
    0
      irc/config.go
  4. 55
    0
      irc/connection_limits/tor.go
  5. 6
    0
      irc/gateways.go
  6. 7
    0
      irc/getters.go
  7. 22
    2
      irc/handlers.go
  8. 5
    0
      irc/roleplay.go
  9. 59
    26
      irc/server.go
  10. 24
    0
      oragono.yaml

+ 44
- 0
docs/MANUAL.md View File

@@ -34,6 +34,8 @@ _Copyright © 2018 Daniel Oaks <daniel@danieloaks.net>_
34 34
 - Commands
35 35
 - Integrating with other software
36 36
     - HOPM
37
+    - ZNC
38
+    - Tor
37 39
 - Acknowledgements
38 40
 
39 41
 
@@ -601,6 +603,48 @@ kline = "DLINE ANDKILL 2h %i :Open proxy found on your host.";
601 603
 Versions of ZNC prior to 1.7 have a [bug](https://github.com/znc/znc/issues/1212) in their SASL implementation that renders them incompatible with Oragono. However, you should be able to authenticate from ZNC using its [NickServ](https://wiki.znc.in/Nickserv) module.
602 604
 
603 605
 
606
+## Tor
607
+
608
+Oragono has code support for adding an .onion address to an IRC server, or operating an IRC server as a Tor hidden service. This is subtle, so you should be familiar with the [Tor Project](https://www.torproject.org/) and the concept of a [hidden service](https://www.torproject.org/docs/tor-onion-service.html.en).
609
+
610
+There are two possible ways to serve Oragono over Tor. One is to add a .onion address to a server that also serves non-Tor clients, and whose IP address is public information. This is relatively straightforward. Add a separate listener, for example `127.0.0.2:6668`, to Oragono's `server.listen`, then add it to `server.tor-listeners.listeners`  Configure Tor like this:
611
+
612
+````
613
+HiddenServiceDir /var/lib/tor/oragono_hidden_service
614
+HiddenServicePort 6667 127.0.0.2:6668
615
+
616
+# these are optional, but can be used to speed up the circuits in the case
617
+# where the server's own IP is public information (clients will remain anonymous):
618
+HiddenServiceNonAnonymousMode 1
619
+HiddenServiceSingleHopMode 1
620
+````
621
+
622
+The second way is to run Oragono as a true hidden service, where the server's actual IP address is a secret. This requires hardening measures on the Oragono side:
623
+
624
+* Oragono should not accept any connections on its public interfaces. You should remove any listener that starts with the address of a public interface, or with `:`, which means "listen on all available interfaces". You should listen only on `127.0.0.1:6667` and a Unix domain socket such as `/hidden_service_sockets/oragono.sock`.
625
+* In this mode, it is especially important that all operator passwords are strong and all operators are trusted (operators have a larger attack surface to deanonymize the server).
626
+* Tor hidden services are at risk of being deanonymized if a client can trick the server into performing a non-Tor network request. Oragono should not perform any such requests (such as hostname resolution or ident lookups) in response to input received over a correctly configured Tor listener. However, Oragono has not been thoroughly audited against such deanonymization attacks --- therefore, Oragono should be deployed with additional sandboxing to protect against this:
627
+  * Oragono should run with no direct network connectivity, e.g., by running in its own Linux network namespace. systemd implements this with the [PrivateNetwork](https://www.freedesktop.org/software/systemd/man/systemd.exec.html) configuration option: add `PrivateNetwork=true` to Oragono's systemd unit file.
628
+  * Since the loopback adapters are local to a specific network namespace, Oragono must be configured to listen on a Unix domain socket that the Tor daemon can connect to. However, distributions typically package Tor with its own hardening profiles, which will restrict which sockets it can connect to. Below is a recipe for configuring this with the official Tor packages for Debian:
629
+
630
+1. Create a directory with `0777` permissions such as `/hidden_service_sockets`.
631
+1. Configure Oragono to listen on `/hidden_service_sockets/oragono.sock`, and add this socket to `server.tor-listeners.listeners`.
632
+1. Ensure that Oragono has no direct network access as described above, e.g., with `PrivateNetwork=true`.
633
+1. Next, modify Tor's apparmor profile so that it can connect to this socket, by adding the line `  /hidden_service_sockets/** rw,` to `/etc/apparmor.d/local/system_tor`.
634
+1. Finally, configure Tor with:
635
+
636
+````
637
+HiddenServiceDir /var/lib/tor/oragono_hidden_service
638
+HiddenServicePort 6667 unix:/hidden_service_sockets/oragono.sock
639
+# DO NOT enable HiddenServiceNonAnonymousMode
640
+````
641
+
642
+Instructions on how client software should connect to an .onion address are outside the scope of this manual. However:
643
+
644
+1. [Hexchat](https://hexchat.github.io/) is known to support .onion addresses, once it has been configured to use a local Tor daemon as a SOCKS proxy (Settings -> Preferences -> Network Setup -> Proxy Server).
645
+1. Pidgin should work with [torsocks](https://trac.torproject.org/projects/tor/wiki/doc/torsocks).
646
+
647
+
604 648
 --------------------------------------------------------------------------------------------
605 649
 
606 650
 

+ 59
- 46
irc/client.go View File

@@ -65,6 +65,7 @@ type Client struct {
65 65
 	idletimer          *IdleTimer
66 66
 	invitedTo          map[string]bool
67 67
 	isDestroyed        bool
68
+	isTor              bool
68 69
 	isQuitting         bool
69 70
 	languages          []string
70 71
 	loginThrottle      connection_limits.GenericThrottle
@@ -117,12 +118,12 @@ type ClientDetails struct {
117 118
 	accountName        string
118 119
 }
119 120
 
120
-// NewClient sets up a new client and starts its goroutine.
121
-func NewClient(server *Server, conn net.Conn, isTLS bool) {
121
+// NewClient sets up a new client and runs its goroutine.
122
+func RunNewClient(server *Server, conn clientConn) {
122 123
 	now := time.Now()
123 124
 	config := server.Config()
124 125
 	fullLineLenLimit := config.Limits.LineLen.Tags + config.Limits.LineLen.Rest
125
-	socket := NewSocket(conn, fullLineLenLimit*2, config.Server.MaxSendQBytes)
126
+	socket := NewSocket(conn.Conn, fullLineLenLimit*2, config.Server.MaxSendQBytes)
126 127
 	client := &Client{
127 128
 		atime:        now,
128 129
 		capabilities: caps.NewSet(),
@@ -131,6 +132,7 @@ func NewClient(server *Server, conn net.Conn, isTLS bool) {
131 132
 		channels:     make(ChannelSet),
132 133
 		ctime:        now,
133 134
 		flags:        modes.NewModeSet(),
135
+		isTor:        conn.IsTor,
134 136
 		loginThrottle: connection_limits.GenericThrottle{
135 137
 			Duration: config.Accounts.LoginThrottling.Duration,
136 138
 			Limit:    config.Accounts.LoginThrottling.MaxAttempts,
@@ -145,58 +147,73 @@ func NewClient(server *Server, conn net.Conn, isTLS bool) {
145 147
 	}
146 148
 	client.languages = server.languages.Default()
147 149
 
148
-	remoteAddr := conn.RemoteAddr()
149
-	client.realIP = utils.AddrToIP(remoteAddr)
150
-	if client.realIP == nil {
151
-		server.logger.Error("internal", "bad remote address", remoteAddr.String())
152
-		return
153
-	}
154 150
 	client.recomputeMaxlens()
155
-	if isTLS {
156
-		client.SetMode(modes.TLS, true)
157 151
 
152
+	if conn.IsTLS {
153
+		client.SetMode(modes.TLS, true)
158 154
 		// error is not useful to us here anyways so we can ignore it
159 155
 		client.certfp, _ = client.socket.CertFP()
160 156
 	}
161
-	if config.Server.CheckIdent && !utils.AddrIsUnix(remoteAddr) {
162
-		_, serverPortString, err := net.SplitHostPort(conn.LocalAddr().String())
163
-		if err != nil {
164
-			server.logger.Error("internal", "bad server address", err.Error())
165
-			return
166
-		}
167
-		serverPort, _ := strconv.Atoi(serverPortString)
168
-		clientHost, clientPortString, err := net.SplitHostPort(conn.RemoteAddr().String())
169
-		if err != nil {
170
-			server.logger.Error("internal", "bad client address", err.Error())
171
-			return
157
+
158
+	if conn.IsTor {
159
+		client.SetMode(modes.TLS, true)
160
+		client.realIP = utils.IPv4LoopbackAddress
161
+		client.rawHostname = config.Server.TorListeners.Vhost
162
+	} else {
163
+		remoteAddr := conn.Conn.RemoteAddr()
164
+		client.realIP = utils.AddrToIP(remoteAddr)
165
+		// Set the hostname for this client
166
+		// (may be overridden by a later PROXY command from stunnel)
167
+		client.rawHostname = utils.LookupHostname(client.realIP.String())
168
+		if config.Server.CheckIdent && !utils.AddrIsUnix(remoteAddr) {
169
+			client.doIdentLookup(conn.Conn)
172 170
 		}
173
-		clientPort, _ := strconv.Atoi(clientPortString)
171
+	}
172
+
173
+	client.run()
174
+}
175
+
176
+func (client *Client) doIdentLookup(conn net.Conn) {
177
+	_, serverPortString, err := net.SplitHostPort(conn.LocalAddr().String())
178
+	if err != nil {
179
+		client.server.logger.Error("internal", "bad server address", err.Error())
180
+		return
181
+	}
182
+	serverPort, _ := strconv.Atoi(serverPortString)
183
+	clientHost, clientPortString, err := net.SplitHostPort(conn.RemoteAddr().String())
184
+	if err != nil {
185
+		client.server.logger.Error("internal", "bad client address", err.Error())
186
+		return
187
+	}
188
+	clientPort, _ := strconv.Atoi(clientPortString)
174 189
 
175
-		client.Notice(client.t("*** Looking up your username"))
176
-		resp, err := ident.Query(clientHost, serverPort, clientPort, IdentTimeoutSeconds)
190
+	client.Notice(client.t("*** Looking up your username"))
191
+	resp, err := ident.Query(clientHost, serverPort, clientPort, IdentTimeoutSeconds)
192
+	if err == nil {
193
+		err := client.SetNames(resp.Identifier, "", true)
177 194
 		if err == nil {
178
-			err := client.SetNames(resp.Identifier, "", true)
179
-			if err == nil {
180
-				client.Notice(client.t("*** Found your username"))
181
-				// we don't need to updateNickMask here since nickMask is not used for anything yet
182
-			} else {
183
-				client.Notice(client.t("*** Got a malformed username, ignoring"))
184
-			}
195
+			client.Notice(client.t("*** Found your username"))
196
+			// we don't need to updateNickMask here since nickMask is not used for anything yet
185 197
 		} else {
186
-			client.Notice(client.t("*** Could not find your username"))
198
+			client.Notice(client.t("*** Got a malformed username, ignoring"))
187 199
 		}
200
+	} else {
201
+		client.Notice(client.t("*** Could not find your username"))
188 202
 	}
189
-	go client.run()
190 203
 }
191 204
 
192 205
 func (client *Client) isAuthorized(config *Config) bool {
193 206
 	saslSent := client.account != ""
194
-	passRequirementMet := (config.Server.passwordBytes == nil) || client.sentPassCommand || (config.Accounts.SkipServerPassword && saslSent)
195
-	if !passRequirementMet {
207
+	// PASS requirement
208
+	if !((config.Server.passwordBytes == nil) || client.sentPassCommand || (config.Accounts.SkipServerPassword && saslSent)) {
196 209
 		return false
197 210
 	}
198
-	saslRequirementMet := !config.Accounts.RequireSasl.Enabled || saslSent || utils.IPInNets(client.IP(), config.Accounts.RequireSasl.exemptedNets)
199
-	return saslRequirementMet
211
+	// Tor connections may be required to authenticate with SASL
212
+	if config.Server.TorListeners.RequireSasl && !saslSent {
213
+		return false
214
+	}
215
+	// finally, enforce require-sasl
216
+	return !config.Accounts.RequireSasl.Enabled || saslSent || utils.IPInNets(client.IP(), config.Accounts.RequireSasl.exemptedNets)
200 217
 }
201 218
 
202 219
 func (client *Client) resetFakelag() {
@@ -296,10 +313,6 @@ func (client *Client) run() {
296 313
 
297 314
 	client.resetFakelag()
298 315
 
299
-	// Set the hostname for this client
300
-	// (may be overridden by a later PROXY command from stunnel)
301
-	client.rawHostname = utils.LookupHostname(client.realIP.String())
302
-
303 316
 	firstLine := true
304 317
 
305 318
 	for {
@@ -884,10 +897,10 @@ func (client *Client) destroy(beingResumed bool) {
884 897
 	}
885 898
 
886 899
 	// remove from connection limits
887
-	ipaddr := client.IP()
888
-	// this check shouldn't be required but eh
889
-	if ipaddr != nil {
890
-		client.server.connectionLimiter.RemoveClient(ipaddr)
900
+	if client.isTor {
901
+		client.server.torLimiter.RemoveClient()
902
+	} else {
903
+		client.server.connectionLimiter.RemoveClient(client.IP())
891 904
 	}
892 905
 
893 906
 	client.server.resumeManager.Delete(client)

+ 23
- 0
irc/config.go View File

@@ -251,6 +251,15 @@ type FakelagConfig struct {
251 251
 	Cooldown          time.Duration
252 252
 }
253 253
 
254
+type TorListenersConfig struct {
255
+	Listeners                 []string
256
+	RequireSasl               bool `yaml:"require-sasl"`
257
+	Vhost                     string
258
+	MaxConnections            int           `yaml:"max-connections"`
259
+	ThrottleDuration          time.Duration `yaml:"throttle-duration"`
260
+	MaxConnectionsPerDuration int           `yaml:"max-connections-per-duration"`
261
+}
262
+
254 263
 // Config defines the overall configuration.
255 264
 type Config struct {
256 265
 	Network struct {
@@ -265,6 +274,7 @@ type Config struct {
265 274
 		Listen               []string
266 275
 		UnixBindMode         os.FileMode                 `yaml:"unix-bind-mode"`
267 276
 		TLSListeners         map[string]*TLSListenConfig `yaml:"tls-listeners"`
277
+		TorListeners         TorListenersConfig          `yaml:"tor-listeners"`
268 278
 		STS                  STSConfig
269 279
 		CheckIdent           bool `yaml:"check-ident"`
270 280
 		MOTD                 string
@@ -806,5 +816,18 @@ func LoadConfig(filename string) (config *Config, err error) {
806 816
 		config.History.ClientLength = 0
807 817
 	}
808 818
 
819
+	for _, listenAddress := range config.Server.TorListeners.Listeners {
820
+		found := false
821
+		for _, configuredListener := range config.Server.Listen {
822
+			if listenAddress == configuredListener {
823
+				found = true
824
+				break
825
+			}
826
+		}
827
+		if !found {
828
+			return nil, fmt.Errorf("%s is configured as a Tor listener, but is not in server.listen", listenAddress)
829
+		}
830
+	}
831
+
809 832
 	return config, nil
810 833
 }

+ 55
- 0
irc/connection_limits/tor.go View File

@@ -0,0 +1,55 @@
1
+// Copyright (c) 2019 Shivaram Lingamneni <slingamn@cs.stanford.edu>
2
+// released under the MIT license
3
+
4
+package connection_limits
5
+
6
+import (
7
+	"errors"
8
+	"sync"
9
+	"time"
10
+)
11
+
12
+var (
13
+	ErrLimitExceeded    = errors.New("too many concurrent connections")
14
+	ErrThrottleExceeded = errors.New("too many recent connection attempts")
15
+)
16
+
17
+// TorLimiter is a combined limiter and throttler for use on connections
18
+// proxied from a Tor hidden service (so we don't have meaningful IPs,
19
+// a notion of CIDR width, etc.)
20
+type TorLimiter struct {
21
+	sync.Mutex
22
+
23
+	numConnections int
24
+	maxConnections int
25
+	throttle       GenericThrottle
26
+}
27
+
28
+func (tl *TorLimiter) Configure(maxConnections int, duration time.Duration, maxConnectionsPerDuration int) {
29
+	tl.Lock()
30
+	defer tl.Unlock()
31
+	tl.maxConnections = maxConnections
32
+	tl.throttle.Duration = duration
33
+	tl.throttle.Limit = maxConnectionsPerDuration
34
+}
35
+
36
+func (tl *TorLimiter) AddClient() error {
37
+	tl.Lock()
38
+	defer tl.Unlock()
39
+
40
+	if tl.maxConnections != 0 && tl.maxConnections <= tl.numConnections {
41
+		return ErrLimitExceeded
42
+	}
43
+	throttled, _ := tl.throttle.Touch()
44
+	if throttled {
45
+		return ErrThrottleExceeded
46
+	}
47
+	tl.numConnections += 1
48
+	return nil
49
+}
50
+
51
+func (tl *TorLimiter) RemoveClient() {
52
+	tl.Lock()
53
+	tl.numConnections -= 1
54
+	tl.Unlock()
55
+}

+ 6
- 0
irc/gateways.go View File

@@ -47,6 +47,12 @@ func (wc *webircConfig) Populate() (err error) {
47 47
 
48 48
 // ApplyProxiedIP applies the given IP to the client.
49 49
 func (client *Client) ApplyProxiedIP(proxiedIP string, tls bool) (success bool) {
50
+	// PROXY and WEBIRC are never accepted from a Tor listener, even if the address itself
51
+	// is whitelisted:
52
+	if client.isTor {
53
+		return false
54
+	}
55
+
50 56
 	// ensure IP is sane
51 57
 	parsedProxiedIP := net.ParseIP(proxiedIP).To16()
52 58
 	if parsedProxiedIP == nil {

+ 7
- 0
irc/getters.go View File

@@ -143,6 +143,13 @@ func (client *Client) SetRegistered() {
143 143
 	client.stateMutex.Unlock()
144 144
 }
145 145
 
146
+func (client *Client) RawHostname() (result string) {
147
+	client.stateMutex.Lock()
148
+	result = client.rawHostname
149
+	client.stateMutex.Unlock()
150
+	return
151
+}
152
+
146 153
 func (client *Client) AwayMessage() (result string) {
147 154
 	client.stateMutex.RLock()
148 155
 	result = client.awayMessage

+ 22
- 2
irc/handlers.go View File

@@ -1884,6 +1884,11 @@ func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
1884 1884
 	targets := strings.Split(msg.Params[0], ",")
1885 1885
 	message := msg.Params[1]
1886 1886
 
1887
+	if client.isTor && isRestrictedCTCPMessage(message) {
1888
+		rb.Add(nil, server.name, "NOTICE", client.t("CTCP messages are disabled over Tor"))
1889
+		return false
1890
+	}
1891
+
1887 1892
 	// split privmsg
1888 1893
 	splitMsg := utils.MakeSplitMessage(message, !client.capabilities.Has(caps.MaxLine))
1889 1894
 
@@ -1930,7 +1935,9 @@ func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
1930 1935
 			msgid := server.generateMessageID()
1931 1936
 			// restrict messages appropriately when +R is set
1932 1937
 			// intentionally make the sending user think the message went through fine
1933
-			if !user.HasMode(modes.RegisteredOnly) || client.LoggedIntoAccount() {
1938
+			allowedPlusR := !user.HasMode(modes.RegisteredOnly) || client.LoggedIntoAccount()
1939
+			allowedTor := !user.isTor || !isRestrictedCTCPMessage(message)
1940
+			if allowedPlusR && allowedTor {
1934 1941
 				user.SendSplitMsgFromClient(msgid, client, clientOnlyTags, "NOTICE", user.nick, splitMsg)
1935 1942
 			}
1936 1943
 			nickMaskString := client.NickMaskString()
@@ -2087,12 +2094,23 @@ func pongHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
2087 2094
 	return false
2088 2095
 }
2089 2096
 
2097
+func isRestrictedCTCPMessage(message string) bool {
2098
+	// block all CTCP privmsgs to Tor clients except for ACTION
2099
+	// DCC can potentially be used for deanonymization, the others for fingerprinting
2100
+	return strings.HasPrefix(message, "\x01") && !strings.HasPrefix(message, "\x01ACTION")
2101
+}
2102
+
2090 2103
 // PRIVMSG <target>{,<target>} <message>
2091 2104
 func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
2092 2105
 	clientOnlyTags := utils.GetClientOnlyTags(msg.Tags)
2093 2106
 	targets := strings.Split(msg.Params[0], ",")
2094 2107
 	message := msg.Params[1]
2095 2108
 
2109
+	if client.isTor && isRestrictedCTCPMessage(message) {
2110
+		rb.Add(nil, server.name, "NOTICE", client.t("CTCP messages are disabled over Tor"))
2111
+		return false
2112
+	}
2113
+
2096 2114
 	// split privmsg
2097 2115
 	splitMsg := utils.MakeSplitMessage(message, !client.capabilities.Has(caps.MaxLine))
2098 2116
 
@@ -2142,7 +2160,9 @@ func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R
2142 2160
 			msgid := server.generateMessageID()
2143 2161
 			// restrict messages appropriately when +R is set
2144 2162
 			// intentionally make the sending user think the message went through fine
2145
-			if !user.HasMode(modes.RegisteredOnly) || client.LoggedIntoAccount() {
2163
+			allowedPlusR := !user.HasMode(modes.RegisteredOnly) || client.LoggedIntoAccount()
2164
+			allowedTor := !user.isTor || !isRestrictedCTCPMessage(message)
2165
+			if allowedPlusR && allowedTor {
2146 2166
 				user.SendSplitMsgFromClient(msgid, client, clientOnlyTags, "PRIVMSG", user.nick, splitMsg)
2147 2167
 			}
2148 2168
 			nickMaskString := client.NickMaskString()

+ 5
- 0
irc/roleplay.go View File

@@ -19,6 +19,11 @@ func sendRoleplayMessage(server *Server, client *Client, source string, targetSt
19 19
 	if isAction {
20 20
 		message = fmt.Sprintf("\x01ACTION %s (%s)\x01", message, client.nick)
21 21
 	} else {
22
+		// block attempts to send CTCP messages to Tor clients
23
+		// TODO(#395) clean this up
24
+		if len(message) != 0 && message[0] == '\x01' {
25
+			return
26
+		}
22 27
 		message = fmt.Sprintf("%s (%s)", message, client.nick)
23 28
 	}
24 29
 

+ 59
- 26
irc/server.go View File

@@ -21,7 +21,6 @@ import (
21 21
 	"time"
22 22
 
23 23
 	"github.com/goshuirc/irc-go/ircfmt"
24
-	"github.com/goshuirc/irc-go/ircmsg"
25 24
 	"github.com/oragono/oragono/irc/caps"
26 25
 	"github.com/oragono/oragono/irc/connection_limits"
27 26
 	"github.com/oragono/oragono/irc/isupport"
@@ -35,10 +34,7 @@ import (
35 34
 
36 35
 var (
37 36
 	// common error line to sub values into
38
-	errorMsg, _ = (&[]ircmsg.IrcMessage{ircmsg.MakeMessage(nil, "", "ERROR", "%s ")}[0]).Line()
39
-
40
-	// common error responses
41
-	couldNotParseIPMsg, _ = (&[]ircmsg.IrcMessage{ircmsg.MakeMessage(nil, "", "ERROR", "Unable to parse your IP address")}[0]).Line()
37
+	errorMsg = "ERROR :%s\r\n"
42 38
 
43 39
 	// supportedUserModesString acts as a cache for when we introduce users
44 40
 	supportedUserModesString = modes.SupportedUserModes.String()
@@ -58,6 +54,7 @@ var (
58 54
 type ListenerWrapper struct {
59 55
 	listener   net.Listener
60 56
 	tlsConfig  *tls.Config
57
+	isTor      bool
61 58
 	shouldStop bool
62 59
 	// protects atomic update of tlsConfig and shouldStop:
63 60
 	configMutex sync.Mutex // tier 1
@@ -92,6 +89,7 @@ type Server struct {
92 89
 	signals                chan os.Signal
93 90
 	snomasks               *SnoManager
94 91
 	store                  *buntdb.DB
92
+	torLimiter             connection_limits.TorLimiter
95 93
 	whoWas                 *WhoWasList
96 94
 	stats                  *Stats
97 95
 	semaphores             *ServerSemaphores
@@ -109,6 +107,7 @@ var (
109 107
 type clientConn struct {
110 108
 	Conn  net.Conn
111 109
 	IsTLS bool
110
+	IsTor bool
112 111
 }
113 112
 
114 113
 // NewServer returns a new Oragono server.
@@ -252,22 +251,27 @@ func (server *Server) Run() {
252 251
 }
253 252
 
254 253
 func (server *Server) acceptClient(conn clientConn) {
255
-	// check IP address
256
-	ipaddr := utils.AddrToIP(conn.Conn.RemoteAddr())
257
-	if ipaddr != nil {
258
-		isBanned, banMsg := server.checkBans(ipaddr)
259
-		if isBanned {
260
-			// this might not show up properly on some clients, but our objective here is just to close the connection out before it has a load impact on us
261
-			conn.Conn.Write([]byte(fmt.Sprintf(errorMsg, banMsg)))
262
-			conn.Conn.Close()
263
-			return
264
-		}
254
+	var isBanned bool
255
+	var banMsg string
256
+	var ipaddr net.IP
257
+	if conn.IsTor {
258
+		ipaddr = utils.IPv4LoopbackAddress
259
+		isBanned, banMsg = server.checkTorLimits()
260
+	} else {
261
+		ipaddr = utils.AddrToIP(conn.Conn.RemoteAddr())
262
+		isBanned, banMsg = server.checkBans(ipaddr)
263
+	}
264
+
265
+	if isBanned {
266
+		// this might not show up properly on some clients, but our objective here is just to close the connection out before it has a load impact on us
267
+		conn.Conn.Write([]byte(fmt.Sprintf(errorMsg, banMsg)))
268
+		conn.Conn.Close()
269
+		return
265 270
 	}
266 271
 
267 272
 	server.logger.Info("localconnect-ip", fmt.Sprintf("Client connecting from %v", ipaddr))
268
-	// prolly don't need to alert snomasks on this, only on connection reg
269 273
 
270
-	NewClient(server, conn.Conn, conn.IsTLS)
274
+	go RunNewClient(server, conn)
271 275
 }
272 276
 
273 277
 func (server *Server) checkBans(ipaddr net.IP) (banned bool, message string) {
@@ -310,12 +314,23 @@ func (server *Server) checkBans(ipaddr net.IP) (banned bool, message string) {
310 314
 	return false, ""
311 315
 }
312 316
 
317
+func (server *Server) checkTorLimits() (banned bool, message string) {
318
+	switch server.torLimiter.AddClient() {
319
+	case connection_limits.ErrLimitExceeded:
320
+		return true, "Too many clients from the Tor network"
321
+	case connection_limits.ErrThrottleExceeded:
322
+		return true, "Exceeded connection throttle for the Tor network"
323
+	default:
324
+		return false, ""
325
+	}
326
+}
327
+
313 328
 //
314 329
 // IRC protocol listeners
315 330
 //
316 331
 
317 332
 // createListener starts a given listener.
318
-func (server *Server) createListener(addr string, tlsConfig *tls.Config, bindMode os.FileMode) (*ListenerWrapper, error) {
333
+func (server *Server) createListener(addr string, tlsConfig *tls.Config, isTor bool, bindMode os.FileMode) (*ListenerWrapper, error) {
319 334
 	// make listener
320 335
 	var listener net.Listener
321 336
 	var err error
@@ -338,6 +353,7 @@ func (server *Server) createListener(addr string, tlsConfig *tls.Config, bindMod
338 353
 	wrapper := ListenerWrapper{
339 354
 		listener:   listener,
340 355
 		tlsConfig:  tlsConfig,
356
+		isTor:      isTor,
341 357
 		shouldStop: false,
342 358
 	}
343 359
 
@@ -349,10 +365,10 @@ func (server *Server) createListener(addr string, tlsConfig *tls.Config, bindMod
349 365
 			conn, err := listener.Accept()
350 366
 
351 367
 			// synchronously access config data:
352
-			// whether TLS is enabled and whether we should stop listening
353 368
 			wrapper.configMutex.Lock()
354 369
 			shouldStop = wrapper.shouldStop
355 370
 			tlsConfig = wrapper.tlsConfig
371
+			isTor = wrapper.isTor
356 372
 			wrapper.configMutex.Unlock()
357 373
 
358 374
 			if err == nil {
@@ -362,6 +378,7 @@ func (server *Server) createListener(addr string, tlsConfig *tls.Config, bindMod
362 378
 				newConn := clientConn{
363 379
 					Conn:  conn,
364 380
 					IsTLS: tlsConfig != nil,
381
+					IsTor: isTor,
365 382
 				}
366 383
 				// hand off the connection
367 384
 				go server.acceptClient(newConn)
@@ -524,7 +541,7 @@ func (client *Client) getWhoisOf(target *Client, rb *ResponseBuffer) {
524 541
 		rb.Add(nil, client.server.name, RPL_WHOISOPERATOR, cnick, tnick, tOper.WhoisLine)
525 542
 	}
526 543
 	if client.HasMode(modes.Operator) || client == target {
527
-		rb.Add(nil, client.server.name, RPL_WHOISACTUALLY, cnick, tnick, fmt.Sprintf("%s@%s", target.username, utils.LookupHostname(target.IPString())), target.IPString(), client.t("Actual user@host, Actual IP"))
544
+		rb.Add(nil, client.server.name, RPL_WHOISACTUALLY, cnick, tnick, fmt.Sprintf("%s@%s", targetInfo.username, target.RawHostname()), target.IPString(), client.t("Actual user@host, Actual IP"))
528 545
 	}
529 546
 	if target.HasMode(modes.TLS) {
530 547
 		rb.Add(nil, client.server.name, RPL_WHOISSECURE, cnick, tnick, client.t("is using a secure connection"))
@@ -639,6 +656,9 @@ func (server *Server) applyConfig(config *Config, initial bool) (err error) {
639 656
 		return err
640 657
 	}
641 658
 
659
+	tlConf := &config.Server.TorListeners
660
+	server.torLimiter.Configure(tlConf.MaxConnections, tlConf.ThrottleDuration, tlConf.MaxConnectionsPerDuration)
661
+
642 662
 	// reload logging config
643 663
 	wasLoggingRawIO := !initial && server.logger.IsLoggingRawIO()
644 664
 	err = server.logger.ApplyConfig(config.Logging)
@@ -931,9 +951,9 @@ func (server *Server) loadDatastore(config *Config) error {
931 951
 }
932 952
 
933 953
 func (server *Server) setupListeners(config *Config) (err error) {
934
-	logListener := func(addr string, tlsconfig *tls.Config) {
954
+	logListener := func(addr string, tlsconfig *tls.Config, isTor bool) {
935 955
 		server.logger.Info("listeners",
936
-			fmt.Sprintf("now listening on %s, tls=%t.", addr, (tlsconfig != nil)),
956
+			fmt.Sprintf("now listening on %s, tls=%t, tor=%t.", addr, (tlsconfig != nil), isTor),
937 957
 		)
938 958
 	}
939 959
 
@@ -943,6 +963,15 @@ func (server *Server) setupListeners(config *Config) (err error) {
943 963
 		return
944 964
 	}
945 965
 
966
+	isTorListener := func(listener string) bool {
967
+		for _, torListener := range config.Server.TorListeners.Listeners {
968
+			if listener == torListener {
969
+				return true
970
+			}
971
+		}
972
+		return false
973
+	}
974
+
946 975
 	// update or destroy all existing listeners
947 976
 	for addr := range server.listeners {
948 977
 		currentListener := server.listeners[addr]
@@ -958,13 +987,16 @@ func (server *Server) setupListeners(config *Config) (err error) {
958 987
 		// its next Accept(). this is like sending over a buffered channel of
959 988
 		// size 1, but where sending a second item overwrites the buffered item
960 989
 		// instead of blocking.
990
+		tlsConfig := tlsListeners[addr]
991
+		isTor := isTorListener(addr)
961 992
 		currentListener.configMutex.Lock()
962 993
 		currentListener.shouldStop = !stillConfigured
963
-		currentListener.tlsConfig = tlsListeners[addr]
994
+		currentListener.tlsConfig = tlsConfig
995
+		currentListener.isTor = isTor
964 996
 		currentListener.configMutex.Unlock()
965 997
 
966 998
 		if stillConfigured {
967
-			logListener(addr, currentListener.tlsConfig)
999
+			logListener(addr, tlsConfig, isTor)
968 1000
 		} else {
969 1001
 			// tell the listener it should stop by interrupting its Accept() call:
970 1002
 			currentListener.listener.Close()
@@ -978,15 +1010,16 @@ func (server *Server) setupListeners(config *Config) (err error) {
978 1010
 		_, exists := server.listeners[newaddr]
979 1011
 		if !exists {
980 1012
 			// make new listener
1013
+			isTor := isTorListener(newaddr)
981 1014
 			tlsConfig := tlsListeners[newaddr]
982
-			listener, listenerErr := server.createListener(newaddr, tlsConfig, config.Server.UnixBindMode)
1015
+			listener, listenerErr := server.createListener(newaddr, tlsConfig, isTor, config.Server.UnixBindMode)
983 1016
 			if listenerErr != nil {
984 1017
 				server.logger.Error("server", "couldn't listen on", newaddr, listenerErr.Error())
985 1018
 				err = listenerErr
986 1019
 				continue
987 1020
 			}
988 1021
 			server.listeners[newaddr] = listener
989
-			logListener(newaddr, tlsConfig)
1022
+			logListener(newaddr, tlsConfig, isTor)
990 1023
 		}
991 1024
 	}
992 1025
 

+ 24
- 0
oragono.yaml View File

@@ -33,6 +33,30 @@ server:
33 33
             key: tls.key
34 34
             cert: tls.crt
35 35
 
36
+    # tor listeners: designate listeners for use by a tor hidden service / .onion address
37
+    # WARNING: if you are running oragono as a pure hidden service, see the
38
+    # anonymization / hardening recommendations in docs/MANUAL.md
39
+    tor-listeners:
40
+        # any connections that come in on these listeners will be considered
41
+        # Tor connections. it is strongly recommended that these listeners *not*
42
+        # be on public interfaces: they should be on 127.0.0.0/8 or unix domain
43
+        listeners:
44
+        #    - "/tmp/oragono_tor_sock"
45
+
46
+        # if this is true, connections from Tor must authenticate with SASL
47
+        require-sasl: false
48
+
49
+        # what hostname should be displayed for Tor connections?
50
+        vhost: "tor-network.onion"
51
+
52
+        # allow at most this many connections at once (0 for no limit):
53
+        max-connections: 64
54
+
55
+        # connection throttling (limit how many connection attempts are allowed at once):
56
+        throttle-duration: 10m
57
+        # set to 0 to disable throttling:
58
+        max-connections-per-duration: 64
59
+
36 60
     # strict transport security, to get clients to automagically use TLS
37 61
     sts:
38 62
         # whether to advertise STS

Loading…
Cancel
Save