Ver código fonte

fix #561, take 2

tags/v2.0.0-rc1
Shivaram Lingamneni 4 anos atrás
pai
commit
50783d5276
5 arquivos alterados com 85 adições e 18 exclusões
  1. 14
    9
      irc/client.go
  2. 13
    5
      irc/config.go
  3. 43
    2
      irc/gateways.go
  4. 11
    2
      irc/server.go
  5. 4
    0
      oragono.yaml

+ 14
- 9
irc/client.go Ver arquivo

190
 }
190
 }
191
 
191
 
192
 // RunClient sets up a new client and runs its goroutine.
192
 // RunClient sets up a new client and runs its goroutine.
193
-func (server *Server) RunClient(conn clientConn) {
193
+func (server *Server) RunClient(conn clientConn, proxyLine string) {
194
 	var isBanned bool
194
 	var isBanned bool
195
 	var banMsg string
195
 	var banMsg string
196
 	var realIP net.IP
196
 	var realIP net.IP
278
 	client.proxiedIP = session.proxiedIP
278
 	client.proxiedIP = session.proxiedIP
279
 
279
 
280
 	server.stats.Add()
280
 	server.stats.Add()
281
-	client.run(session)
281
+	client.run(session, proxyLine)
282
 }
282
 }
283
 
283
 
284
 func (client *Client) doIdentLookup(conn net.Conn) {
284
 func (client *Client) doIdentLookup(conn net.Conn) {
371
 	return languageManager.Translate(client.Languages(), originalString)
371
 	return languageManager.Translate(client.Languages(), originalString)
372
 }
372
 }
373
 
373
 
374
-//
375
-// command goroutine
376
-//
377
-
378
-func (client *Client) run(session *Session) {
374
+// main client goroutine: read lines and execute the corresponding commands
375
+// `proxyLine` is the PROXY-before-TLS line, if there was one
376
+func (client *Client) run(session *Session, proxyLine string) {
379
 
377
 
380
 	defer func() {
378
 	defer func() {
381
 		if r := recover(); r != nil {
379
 		if r := recover(); r != nil {
414
 	for {
412
 	for {
415
 		maxlenRest := session.MaxlenRest()
413
 		maxlenRest := session.MaxlenRest()
416
 
414
 
417
-		line, err := session.socket.Read()
415
+		var line string
416
+		var err error
417
+		if proxyLine == "" {
418
+			line, err = session.socket.Read()
419
+		} else {
420
+			line = proxyLine // pretend we're just now receiving the proxy-before-TLS line
421
+			proxyLine = ""
422
+		}
418
 		if err != nil {
423
 		if err != nil {
419
 			quitMessage := "connection closed"
424
 			quitMessage := "connection closed"
420
 			if err == errReadQ {
425
 			if err == errReadQ {
483
 			break
488
 			break
484
 		} else if session.client != client {
489
 		} else if session.client != client {
485
 			// bouncer reattach
490
 			// bouncer reattach
486
-			go session.client.run(session)
491
+			go session.client.run(session, "")
487
 			break
492
 			break
488
 		}
493
 		}
489
 	}
494
 	}

+ 13
- 5
irc/config.go Ver arquivo

39
 
39
 
40
 // TLSListenConfig defines configuration options for listening on TLS.
40
 // TLSListenConfig defines configuration options for listening on TLS.
41
 type TLSListenConfig struct {
41
 type TLSListenConfig struct {
42
-	Cert string
43
-	Key  string
42
+	Cert  string
43
+	Key   string
44
+	Proxy bool
44
 }
45
 }
45
 
46
 
46
 // This is the YAML-deserializable type of the value of the `Server.Listeners` map
47
 // This is the YAML-deserializable type of the value of the `Server.Listeners` map
53
 // listenerConfig is the config governing a particular listener (bound address),
54
 // listenerConfig is the config governing a particular listener (bound address),
54
 // in particular whether it has TLS or Tor (or both) enabled.
55
 // in particular whether it has TLS or Tor (or both) enabled.
55
 type listenerConfig struct {
56
 type listenerConfig struct {
56
-	TLSConfig *tls.Config
57
-	IsTor     bool
58
-	IsSTSOnly bool
57
+	TLSConfig  *tls.Config
58
+	IsTor      bool
59
+	IsSTSOnly  bool
60
+	IsTLSProxy bool
59
 }
61
 }
60
 
62
 
61
 type AccountConfig struct {
63
 type AccountConfig struct {
529
 					return err
531
 					return err
530
 				}
532
 				}
531
 				lconf.TLSConfig = tlsConfig
533
 				lconf.TLSConfig = tlsConfig
534
+				lconf.IsTLSProxy = block.TLS.Proxy
532
 			}
535
 			}
533
 			listeners[addr] = lconf
536
 			listeners[addr] = lconf
534
 		}
537
 		}
848
 		return nil, err
851
 		return nil, err
849
 	}
852
 	}
850
 
853
 
854
+	err = config.prepareListeners()
855
+	if err != nil {
856
+		return nil, fmt.Errorf("failed to prepare listeners: %v", err)
857
+	}
858
+
851
 	return config, nil
859
 	return config, nil
852
 }
860
 }

+ 43
- 2
irc/gateways.go Ver arquivo

10
 	"fmt"
10
 	"fmt"
11
 	"net"
11
 	"net"
12
 	"strings"
12
 	"strings"
13
+	"time"
13
 
14
 
14
 	"github.com/oragono/oragono/irc/modes"
15
 	"github.com/oragono/oragono/irc/modes"
15
 	"github.com/oragono/oragono/irc/utils"
16
 	"github.com/oragono/oragono/irc/utils"
20
 	errBadProxyLine      = errors.New("Invalid PROXY/WEBIRC command")
21
 	errBadProxyLine      = errors.New("Invalid PROXY/WEBIRC command")
21
 )
22
 )
22
 
23
 
24
+const (
25
+	// https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
26
+	// "a 108-byte buffer is always enough to store all the line and a trailing zero
27
+	// for string processing."
28
+	maxProxyLineLen = 107
29
+)
30
+
23
 type webircConfig struct {
31
 type webircConfig struct {
24
 	PasswordString string `yaml:"password"`
32
 	PasswordString string `yaml:"password"`
25
 	Password       []byte `yaml:"password-bytes"`
33
 	Password       []byte `yaml:"password-bytes"`
75
 
83
 
76
 	client.stateMutex.Lock()
84
 	client.stateMutex.Lock()
77
 	defer client.stateMutex.Unlock()
85
 	defer client.stateMutex.Unlock()
78
-	session.proxiedIP = parsedProxiedIP
79
 	client.proxiedIP = parsedProxiedIP
86
 	client.proxiedIP = parsedProxiedIP
80
-	session.rawHostname = rawHostname
81
 	client.rawHostname = rawHostname
87
 	client.rawHostname = rawHostname
88
+	session.proxiedIP = parsedProxiedIP
89
+	session.rawHostname = rawHostname
82
 	client.cloakedHostname = cloakedHostname
90
 	client.cloakedHostname = cloakedHostname
83
 	// nickmask will be updated when the client completes registration
91
 	// nickmask will be updated when the client completes registration
84
 	// set tls info
92
 	// set tls info
118
 		return errBadGatewayAddress
126
 		return errBadGatewayAddress
119
 	}
127
 	}
120
 }
128
 }
129
+
130
+// read a PROXY line one byte at a time, to ensure we don't read anything beyond
131
+// that into a buffer, which would break the TLS handshake
132
+func readRawProxyLine(conn net.Conn) (result string) {
133
+	// normally this is covered by ping timeouts, but we're doing this outside
134
+	// of the normal client goroutine:
135
+	conn.SetDeadline(time.Now().Add(time.Minute))
136
+	defer conn.SetDeadline(time.Time{})
137
+
138
+	var buf [maxProxyLineLen]byte
139
+	oneByte := make([]byte, 1)
140
+	i := 0
141
+	for i < maxProxyLineLen {
142
+		n, err := conn.Read(oneByte)
143
+		if err != nil {
144
+			return
145
+		} else if n == 1 {
146
+			buf[i] = oneByte[0]
147
+			if buf[i] == '\n' {
148
+				candidate := string(buf[0 : i+1])
149
+				if strings.HasPrefix(candidate, "PROXY") {
150
+					return candidate
151
+				} else {
152
+					return
153
+				}
154
+			}
155
+			i += 1
156
+		}
157
+	}
158
+
159
+	// no \r\n, fail out
160
+	return
161
+}

+ 11
- 2
irc/server.go Ver arquivo

307
 				listener.Close()
307
 				listener.Close()
308
 				return
308
 				return
309
 			} else if err == nil {
309
 			} else if err == nil {
310
+				var proxyLine string
311
+				if conf.IsTLSProxy {
312
+					proxyLine = readRawProxyLine(conn)
313
+					if proxyLine == "" {
314
+						server.logger.Error("internal", "bad TLS-proxy line from", addr)
315
+						conn.Close()
316
+						continue
317
+					}
318
+				}
310
 				if conf.TLSConfig != nil {
319
 				if conf.TLSConfig != nil {
311
 					conn = tls.Server(conn, conf.TLSConfig)
320
 					conn = tls.Server(conn, conf.TLSConfig)
312
 				}
321
 				}
315
 					Config: conf,
324
 					Config: conf,
316
 				}
325
 				}
317
 				// hand off the connection
326
 				// hand off the connection
318
-				go server.RunClient(newConn)
327
+				go server.RunClient(newConn, proxyLine)
319
 			} else {
328
 			} else {
320
 				server.logger.Error("internal", "accept error", addr, err.Error())
329
 				server.logger.Error("internal", "accept error", addr, err.Error())
321
 			}
330
 			}
868
 func (server *Server) setupListeners(config *Config) (err error) {
877
 func (server *Server) setupListeners(config *Config) (err error) {
869
 	logListener := func(addr string, config listenerConfig) {
878
 	logListener := func(addr string, config listenerConfig) {
870
 		server.logger.Info("listeners",
879
 		server.logger.Info("listeners",
871
-			fmt.Sprintf("now listening on %s, tls=%t, tor=%t.", addr, (config.TLSConfig != nil), config.IsTor),
880
+			fmt.Sprintf("now listening on %s, tls=%t, tlsproxy=%t, tor=%t.", addr, (config.TLSConfig != nil), config.IsTLSProxy, config.IsTor),
872
 		)
881
 		)
873
 	}
882
 	}
874
 
883
 

+ 4
- 0
oragono.yaml Ver arquivo

30
             tls:
30
             tls:
31
                 key: tls.key
31
                 key: tls.key
32
                 cert: tls.crt
32
                 cert: tls.crt
33
+                # 'proxy' should typically be false. It's only for Kubernetes-style load
34
+                # balancing that does not terminate TLS, but sends an initial PROXY line
35
+                # in plaintext.
36
+                proxy: false
33
 
37
 
34
         # Example of a Unix domain socket for proxying:
38
         # Example of a Unix domain socket for proxying:
35
         # "/tmp/oragono_sock":
39
         # "/tmp/oragono_sock":

Carregando…
Cancelar
Salvar