Просмотр исходного кода

add support for tor (#369)

tags/v1.1.0-rc1
Shivaram Lingamneni 5 лет назад
Родитель
Сommit
b0f89062fa
10 измененных файлов: 304 добавлений и 74 удалений
  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 Просмотреть файл

34
 - Commands
34
 - Commands
35
 - Integrating with other software
35
 - Integrating with other software
36
     - HOPM
36
     - HOPM
37
+    - ZNC
38
+    - Tor
37
 - Acknowledgements
39
 - Acknowledgements
38
 
40
 
39
 
41
 
601
 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.
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 Просмотреть файл

65
 	idletimer          *IdleTimer
65
 	idletimer          *IdleTimer
66
 	invitedTo          map[string]bool
66
 	invitedTo          map[string]bool
67
 	isDestroyed        bool
67
 	isDestroyed        bool
68
+	isTor              bool
68
 	isQuitting         bool
69
 	isQuitting         bool
69
 	languages          []string
70
 	languages          []string
70
 	loginThrottle      connection_limits.GenericThrottle
71
 	loginThrottle      connection_limits.GenericThrottle
117
 	accountName        string
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
 	now := time.Now()
123
 	now := time.Now()
123
 	config := server.Config()
124
 	config := server.Config()
124
 	fullLineLenLimit := config.Limits.LineLen.Tags + config.Limits.LineLen.Rest
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
 	client := &Client{
127
 	client := &Client{
127
 		atime:        now,
128
 		atime:        now,
128
 		capabilities: caps.NewSet(),
129
 		capabilities: caps.NewSet(),
131
 		channels:     make(ChannelSet),
132
 		channels:     make(ChannelSet),
132
 		ctime:        now,
133
 		ctime:        now,
133
 		flags:        modes.NewModeSet(),
134
 		flags:        modes.NewModeSet(),
135
+		isTor:        conn.IsTor,
134
 		loginThrottle: connection_limits.GenericThrottle{
136
 		loginThrottle: connection_limits.GenericThrottle{
135
 			Duration: config.Accounts.LoginThrottling.Duration,
137
 			Duration: config.Accounts.LoginThrottling.Duration,
136
 			Limit:    config.Accounts.LoginThrottling.MaxAttempts,
138
 			Limit:    config.Accounts.LoginThrottling.MaxAttempts,
145
 	}
147
 	}
146
 	client.languages = server.languages.Default()
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
 	client.recomputeMaxlens()
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
 		// error is not useful to us here anyways so we can ignore it
154
 		// error is not useful to us here anyways so we can ignore it
159
 		client.certfp, _ = client.socket.CertFP()
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
 		if err == nil {
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
 		} else {
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
 func (client *Client) isAuthorized(config *Config) bool {
205
 func (client *Client) isAuthorized(config *Config) bool {
193
 	saslSent := client.account != ""
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
 		return false
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
 func (client *Client) resetFakelag() {
219
 func (client *Client) resetFakelag() {
296
 
313
 
297
 	client.resetFakelag()
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
 	firstLine := true
316
 	firstLine := true
304
 
317
 
305
 	for {
318
 	for {
884
 	}
897
 	}
885
 
898
 
886
 	// remove from connection limits
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
 	client.server.resumeManager.Delete(client)
906
 	client.server.resumeManager.Delete(client)

+ 23
- 0
irc/config.go Просмотреть файл

251
 	Cooldown          time.Duration
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
 // Config defines the overall configuration.
263
 // Config defines the overall configuration.
255
 type Config struct {
264
 type Config struct {
256
 	Network struct {
265
 	Network struct {
265
 		Listen               []string
274
 		Listen               []string
266
 		UnixBindMode         os.FileMode                 `yaml:"unix-bind-mode"`
275
 		UnixBindMode         os.FileMode                 `yaml:"unix-bind-mode"`
267
 		TLSListeners         map[string]*TLSListenConfig `yaml:"tls-listeners"`
276
 		TLSListeners         map[string]*TLSListenConfig `yaml:"tls-listeners"`
277
+		TorListeners         TorListenersConfig          `yaml:"tor-listeners"`
268
 		STS                  STSConfig
278
 		STS                  STSConfig
269
 		CheckIdent           bool `yaml:"check-ident"`
279
 		CheckIdent           bool `yaml:"check-ident"`
270
 		MOTD                 string
280
 		MOTD                 string
806
 		config.History.ClientLength = 0
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
 	return config, nil
832
 	return config, nil
810
 }
833
 }

+ 55
- 0
irc/connection_limits/tor.go Просмотреть файл

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 Просмотреть файл

47
 
47
 
48
 // ApplyProxiedIP applies the given IP to the client.
48
 // ApplyProxiedIP applies the given IP to the client.
49
 func (client *Client) ApplyProxiedIP(proxiedIP string, tls bool) (success bool) {
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
 	// ensure IP is sane
56
 	// ensure IP is sane
51
 	parsedProxiedIP := net.ParseIP(proxiedIP).To16()
57
 	parsedProxiedIP := net.ParseIP(proxiedIP).To16()
52
 	if parsedProxiedIP == nil {
58
 	if parsedProxiedIP == nil {

+ 7
- 0
irc/getters.go Просмотреть файл

143
 	client.stateMutex.Unlock()
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
 func (client *Client) AwayMessage() (result string) {
153
 func (client *Client) AwayMessage() (result string) {
147
 	client.stateMutex.RLock()
154
 	client.stateMutex.RLock()
148
 	result = client.awayMessage
155
 	result = client.awayMessage

+ 22
- 2
irc/handlers.go Просмотреть файл

1884
 	targets := strings.Split(msg.Params[0], ",")
1884
 	targets := strings.Split(msg.Params[0], ",")
1885
 	message := msg.Params[1]
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
 	// split privmsg
1892
 	// split privmsg
1888
 	splitMsg := utils.MakeSplitMessage(message, !client.capabilities.Has(caps.MaxLine))
1893
 	splitMsg := utils.MakeSplitMessage(message, !client.capabilities.Has(caps.MaxLine))
1889
 
1894
 
1930
 			msgid := server.generateMessageID()
1935
 			msgid := server.generateMessageID()
1931
 			// restrict messages appropriately when +R is set
1936
 			// restrict messages appropriately when +R is set
1932
 			// intentionally make the sending user think the message went through fine
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
 				user.SendSplitMsgFromClient(msgid, client, clientOnlyTags, "NOTICE", user.nick, splitMsg)
1941
 				user.SendSplitMsgFromClient(msgid, client, clientOnlyTags, "NOTICE", user.nick, splitMsg)
1935
 			}
1942
 			}
1936
 			nickMaskString := client.NickMaskString()
1943
 			nickMaskString := client.NickMaskString()
2087
 	return false
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
 // PRIVMSG <target>{,<target>} <message>
2103
 // PRIVMSG <target>{,<target>} <message>
2091
 func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
2104
 func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
2092
 	clientOnlyTags := utils.GetClientOnlyTags(msg.Tags)
2105
 	clientOnlyTags := utils.GetClientOnlyTags(msg.Tags)
2093
 	targets := strings.Split(msg.Params[0], ",")
2106
 	targets := strings.Split(msg.Params[0], ",")
2094
 	message := msg.Params[1]
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
 	// split privmsg
2114
 	// split privmsg
2097
 	splitMsg := utils.MakeSplitMessage(message, !client.capabilities.Has(caps.MaxLine))
2115
 	splitMsg := utils.MakeSplitMessage(message, !client.capabilities.Has(caps.MaxLine))
2098
 
2116
 
2142
 			msgid := server.generateMessageID()
2160
 			msgid := server.generateMessageID()
2143
 			// restrict messages appropriately when +R is set
2161
 			// restrict messages appropriately when +R is set
2144
 			// intentionally make the sending user think the message went through fine
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
 				user.SendSplitMsgFromClient(msgid, client, clientOnlyTags, "PRIVMSG", user.nick, splitMsg)
2166
 				user.SendSplitMsgFromClient(msgid, client, clientOnlyTags, "PRIVMSG", user.nick, splitMsg)
2147
 			}
2167
 			}
2148
 			nickMaskString := client.NickMaskString()
2168
 			nickMaskString := client.NickMaskString()

+ 5
- 0
irc/roleplay.go Просмотреть файл

19
 	if isAction {
19
 	if isAction {
20
 		message = fmt.Sprintf("\x01ACTION %s (%s)\x01", message, client.nick)
20
 		message = fmt.Sprintf("\x01ACTION %s (%s)\x01", message, client.nick)
21
 	} else {
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
 		message = fmt.Sprintf("%s (%s)", message, client.nick)
27
 		message = fmt.Sprintf("%s (%s)", message, client.nick)
23
 	}
28
 	}
24
 
29
 

+ 59
- 26
irc/server.go Просмотреть файл

21
 	"time"
21
 	"time"
22
 
22
 
23
 	"github.com/goshuirc/irc-go/ircfmt"
23
 	"github.com/goshuirc/irc-go/ircfmt"
24
-	"github.com/goshuirc/irc-go/ircmsg"
25
 	"github.com/oragono/oragono/irc/caps"
24
 	"github.com/oragono/oragono/irc/caps"
26
 	"github.com/oragono/oragono/irc/connection_limits"
25
 	"github.com/oragono/oragono/irc/connection_limits"
27
 	"github.com/oragono/oragono/irc/isupport"
26
 	"github.com/oragono/oragono/irc/isupport"
35
 
34
 
36
 var (
35
 var (
37
 	// common error line to sub values into
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
 	// supportedUserModesString acts as a cache for when we introduce users
39
 	// supportedUserModesString acts as a cache for when we introduce users
44
 	supportedUserModesString = modes.SupportedUserModes.String()
40
 	supportedUserModesString = modes.SupportedUserModes.String()
58
 type ListenerWrapper struct {
54
 type ListenerWrapper struct {
59
 	listener   net.Listener
55
 	listener   net.Listener
60
 	tlsConfig  *tls.Config
56
 	tlsConfig  *tls.Config
57
+	isTor      bool
61
 	shouldStop bool
58
 	shouldStop bool
62
 	// protects atomic update of tlsConfig and shouldStop:
59
 	// protects atomic update of tlsConfig and shouldStop:
63
 	configMutex sync.Mutex // tier 1
60
 	configMutex sync.Mutex // tier 1
92
 	signals                chan os.Signal
89
 	signals                chan os.Signal
93
 	snomasks               *SnoManager
90
 	snomasks               *SnoManager
94
 	store                  *buntdb.DB
91
 	store                  *buntdb.DB
92
+	torLimiter             connection_limits.TorLimiter
95
 	whoWas                 *WhoWasList
93
 	whoWas                 *WhoWasList
96
 	stats                  *Stats
94
 	stats                  *Stats
97
 	semaphores             *ServerSemaphores
95
 	semaphores             *ServerSemaphores
109
 type clientConn struct {
107
 type clientConn struct {
110
 	Conn  net.Conn
108
 	Conn  net.Conn
111
 	IsTLS bool
109
 	IsTLS bool
110
+	IsTor bool
112
 }
111
 }
113
 
112
 
114
 // NewServer returns a new Oragono server.
113
 // NewServer returns a new Oragono server.
252
 }
251
 }
253
 
252
 
254
 func (server *Server) acceptClient(conn clientConn) {
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
 	server.logger.Info("localconnect-ip", fmt.Sprintf("Client connecting from %v", ipaddr))
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
 func (server *Server) checkBans(ipaddr net.IP) (banned bool, message string) {
277
 func (server *Server) checkBans(ipaddr net.IP) (banned bool, message string) {
310
 	return false, ""
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
 // IRC protocol listeners
329
 // IRC protocol listeners
315
 //
330
 //
316
 
331
 
317
 // createListener starts a given listener.
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
 	// make listener
334
 	// make listener
320
 	var listener net.Listener
335
 	var listener net.Listener
321
 	var err error
336
 	var err error
338
 	wrapper := ListenerWrapper{
353
 	wrapper := ListenerWrapper{
339
 		listener:   listener,
354
 		listener:   listener,
340
 		tlsConfig:  tlsConfig,
355
 		tlsConfig:  tlsConfig,
356
+		isTor:      isTor,
341
 		shouldStop: false,
357
 		shouldStop: false,
342
 	}
358
 	}
343
 
359
 
349
 			conn, err := listener.Accept()
365
 			conn, err := listener.Accept()
350
 
366
 
351
 			// synchronously access config data:
367
 			// synchronously access config data:
352
-			// whether TLS is enabled and whether we should stop listening
353
 			wrapper.configMutex.Lock()
368
 			wrapper.configMutex.Lock()
354
 			shouldStop = wrapper.shouldStop
369
 			shouldStop = wrapper.shouldStop
355
 			tlsConfig = wrapper.tlsConfig
370
 			tlsConfig = wrapper.tlsConfig
371
+			isTor = wrapper.isTor
356
 			wrapper.configMutex.Unlock()
372
 			wrapper.configMutex.Unlock()
357
 
373
 
358
 			if err == nil {
374
 			if err == nil {
362
 				newConn := clientConn{
378
 				newConn := clientConn{
363
 					Conn:  conn,
379
 					Conn:  conn,
364
 					IsTLS: tlsConfig != nil,
380
 					IsTLS: tlsConfig != nil,
381
+					IsTor: isTor,
365
 				}
382
 				}
366
 				// hand off the connection
383
 				// hand off the connection
367
 				go server.acceptClient(newConn)
384
 				go server.acceptClient(newConn)
524
 		rb.Add(nil, client.server.name, RPL_WHOISOPERATOR, cnick, tnick, tOper.WhoisLine)
541
 		rb.Add(nil, client.server.name, RPL_WHOISOPERATOR, cnick, tnick, tOper.WhoisLine)
525
 	}
542
 	}
526
 	if client.HasMode(modes.Operator) || client == target {
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
 	if target.HasMode(modes.TLS) {
546
 	if target.HasMode(modes.TLS) {
530
 		rb.Add(nil, client.server.name, RPL_WHOISSECURE, cnick, tnick, client.t("is using a secure connection"))
547
 		rb.Add(nil, client.server.name, RPL_WHOISSECURE, cnick, tnick, client.t("is using a secure connection"))
639
 		return err
656
 		return err
640
 	}
657
 	}
641
 
658
 
659
+	tlConf := &config.Server.TorListeners
660
+	server.torLimiter.Configure(tlConf.MaxConnections, tlConf.ThrottleDuration, tlConf.MaxConnectionsPerDuration)
661
+
642
 	// reload logging config
662
 	// reload logging config
643
 	wasLoggingRawIO := !initial && server.logger.IsLoggingRawIO()
663
 	wasLoggingRawIO := !initial && server.logger.IsLoggingRawIO()
644
 	err = server.logger.ApplyConfig(config.Logging)
664
 	err = server.logger.ApplyConfig(config.Logging)
931
 }
951
 }
932
 
952
 
933
 func (server *Server) setupListeners(config *Config) (err error) {
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
 		server.logger.Info("listeners",
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
 		return
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
 	// update or destroy all existing listeners
975
 	// update or destroy all existing listeners
947
 	for addr := range server.listeners {
976
 	for addr := range server.listeners {
948
 		currentListener := server.listeners[addr]
977
 		currentListener := server.listeners[addr]
958
 		// its next Accept(). this is like sending over a buffered channel of
987
 		// its next Accept(). this is like sending over a buffered channel of
959
 		// size 1, but where sending a second item overwrites the buffered item
988
 		// size 1, but where sending a second item overwrites the buffered item
960
 		// instead of blocking.
989
 		// instead of blocking.
990
+		tlsConfig := tlsListeners[addr]
991
+		isTor := isTorListener(addr)
961
 		currentListener.configMutex.Lock()
992
 		currentListener.configMutex.Lock()
962
 		currentListener.shouldStop = !stillConfigured
993
 		currentListener.shouldStop = !stillConfigured
963
-		currentListener.tlsConfig = tlsListeners[addr]
994
+		currentListener.tlsConfig = tlsConfig
995
+		currentListener.isTor = isTor
964
 		currentListener.configMutex.Unlock()
996
 		currentListener.configMutex.Unlock()
965
 
997
 
966
 		if stillConfigured {
998
 		if stillConfigured {
967
-			logListener(addr, currentListener.tlsConfig)
999
+			logListener(addr, tlsConfig, isTor)
968
 		} else {
1000
 		} else {
969
 			// tell the listener it should stop by interrupting its Accept() call:
1001
 			// tell the listener it should stop by interrupting its Accept() call:
970
 			currentListener.listener.Close()
1002
 			currentListener.listener.Close()
978
 		_, exists := server.listeners[newaddr]
1010
 		_, exists := server.listeners[newaddr]
979
 		if !exists {
1011
 		if !exists {
980
 			// make new listener
1012
 			// make new listener
1013
+			isTor := isTorListener(newaddr)
981
 			tlsConfig := tlsListeners[newaddr]
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
 			if listenerErr != nil {
1016
 			if listenerErr != nil {
984
 				server.logger.Error("server", "couldn't listen on", newaddr, listenerErr.Error())
1017
 				server.logger.Error("server", "couldn't listen on", newaddr, listenerErr.Error())
985
 				err = listenerErr
1018
 				err = listenerErr
986
 				continue
1019
 				continue
987
 			}
1020
 			}
988
 			server.listeners[newaddr] = listener
1021
 			server.listeners[newaddr] = listener
989
-			logListener(newaddr, tlsConfig)
1022
+			logListener(newaddr, tlsConfig, isTor)
990
 		}
1023
 		}
991
 	}
1024
 	}
992
 
1025
 

+ 24
- 0
oragono.yaml Просмотреть файл

33
             key: tls.key
33
             key: tls.key
34
             cert: tls.crt
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
     # strict transport security, to get clients to automagically use TLS
60
     # strict transport security, to get clients to automagically use TLS
37
     sts:
61
     sts:
38
         # whether to advertise STS
62
         # whether to advertise STS

Загрузка…
Отмена
Сохранить