Procházet zdrojové kódy

Merge pull request #1608 from slingamn/sni.1

close remaining 2.6 issues
tags/v2.6.0-rc1
Shivaram Lingamneni před 3 roky
rodič
revize
639817f014
Žádný účet není propojen s e-mailovou adresou tvůrce revize
8 změnil soubory, kde provedl 156 přidání a 39 odebrání
  1. 2
    0
      default.yaml
  2. 15
    0
      docs/MANUAL.md
  3. 9
    2
      irc/chanserv.go
  4. 54
    23
      irc/config.go
  5. 1
    1
      irc/mysql/history.go
  6. 12
    3
      irc/nickserv.go
  7. 61
    10
      irc/uban.go
  8. 2
    0
      traditional.yaml

+ 2
- 0
default.yaml Zobrazit soubor

@@ -49,6 +49,8 @@ server:
49 49
 
50 50
         # The standard SSL/TLS port for IRC is 6697. This will listen on all interfaces:
51 51
         ":6697":
52
+            # this is a standard TLS configuration with a single certificate;
53
+            # see the manual for instructions on how to configure SNI
52 54
             tls:
53 55
                 cert: fullchain.pem
54 56
                 key: privkey.pem

+ 15
- 0
docs/MANUAL.md Zobrazit soubor

@@ -49,6 +49,7 @@ _Copyright © Daniel Oaks <daniel@danieloaks.net>, Shivaram Lingamneni <slingamn
49 49
     - [Redirect from plaintext to TLS](#how-can-i-redirect-users-from-plaintext-to-tls)
50 50
     - [Reverse proxies](#reverse-proxies)
51 51
     - [Client certificates](#client-certificates)
52
+    - [SNI](#sni)
52 53
 - [Modes](#modes)
53 54
     - [User Modes](#user-modes)
54 55
     - [Channel Modes](#channel-modes)
@@ -606,6 +607,20 @@ Oragono supports authenticating to user accounts via TLS client certificates. Th
606 607
 
607 608
 Client certificates are not supported over websockets due to a [Chrome bug](https://bugs.chromium.org/p/chromium/issues/detail?id=329884).
608 609
 
610
+## SNI
611
+
612
+Oragono supports [SNI](https://en.wikipedia.org/wiki/Server_Name_Indication); this is useful if you have multiple domain names for your server, with different certificates covering different domain names. Configure your TLS listener like this:
613
+
614
+```yaml
615
+        ":6697":
616
+            tls-certificates:
617
+                -
618
+                    cert: cert1.pem
619
+                    key:  key1.pem
620
+                -
621
+                    cert: cert2.pem
622
+                    key:  key2.pem
623
+```
609 624
 
610 625
 --------------------------------------------------------------------------------------------
611 626
 

+ 9
- 2
irc/chanserv.go Zobrazit soubor

@@ -79,7 +79,9 @@ AMODE lists or modifies persistent mode settings that affect channel members.
79 79
 For example, $bAMODE #channel +o dan$b grants the holder of the "dan"
80 80
 account the +o operator mode every time they join #channel. To list current
81 81
 accounts and modes, use $bAMODE #channel$b. Note that users are always
82
-referenced by their registered account names, not their nicknames.`,
82
+referenced by their registered account names, not their nicknames.
83
+The permissions hierarchy for adding and removing modes is the same as in
84
+the ordinary /MODE command.`,
83 85
 			helpShort: `$bAMODE$b modifies persistent mode settings for channel members.`,
84 86
 			enabled:   chanregEnabled,
85 87
 			minParams: 1,
@@ -144,7 +146,6 @@ If no regex is provided, all registered channels are returned.`,
144 146
 INFO displays info about a registered channel.`,
145 147
 			helpShort: `$bINFO$b displays info about a registered channel.`,
146 148
 			enabled:   chanregEnabled,
147
-			minParams: 1,
148 149
 		},
149 150
 		"get": {
150 151
 			handler: csGetHandler,
@@ -743,6 +744,12 @@ func csListHandler(service *ircService, server *Server, client *Client, command
743 744
 }
744 745
 
745 746
 func csInfoHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
747
+	if len(params) == 0 {
748
+		// #765
749
+		listRegisteredChannels(service, client.Account(), rb)
750
+		return
751
+	}
752
+
746 753
 	chname, err := CasefoldChannel(params[0])
747 754
 	if err != nil {
748 755
 		service.Notice(rb, client.t("Invalid channel name"))

+ 54
- 23
irc/config.go Zobrazit soubor

@@ -8,6 +8,7 @@ package irc
8 8
 import (
9 9
 	"bytes"
10 10
 	"crypto/tls"
11
+	"crypto/x509"
11 12
 	"errors"
12 13
 	"fmt"
13 14
 	"io"
@@ -54,12 +55,15 @@ type TLSListenConfig struct {
54 55
 
55 56
 // This is the YAML-deserializable type of the value of the `Server.Listeners` map
56 57
 type listenerConfigBlock struct {
57
-	TLS       TLSListenConfig
58
-	Proxy     bool
59
-	Tor       bool
60
-	STSOnly   bool `yaml:"sts-only"`
61
-	WebSocket bool
62
-	HideSTS   bool `yaml:"hide-sts"`
58
+	// normal TLS configuration, with a single certificate:
59
+	TLS TLSListenConfig
60
+	// SNI configuration, with multiple certificates:
61
+	TLSCertificates []TLSListenConfig `yaml:"tls-certificates"`
62
+	Proxy           bool
63
+	Tor             bool
64
+	STSOnly         bool `yaml:"sts-only"`
65
+	WebSocket       bool
66
+	HideSTS         bool `yaml:"hide-sts"`
63 67
 }
64 68
 
65 69
 type HistoryCutoff uint
@@ -536,11 +540,10 @@ type Config struct {
536 540
 		passwordBytes  []byte
537 541
 		Name           string
538 542
 		nameCasefolded string
539
-		// Listeners is the new style for configuring listeners:
540
-		Listeners    map[string]listenerConfigBlock
541
-		UnixBindMode os.FileMode        `yaml:"unix-bind-mode"`
542
-		TorListeners TorListenersConfig `yaml:"tor-listeners"`
543
-		WebSockets   struct {
543
+		Listeners      map[string]listenerConfigBlock
544
+		UnixBindMode   os.FileMode        `yaml:"unix-bind-mode"`
545
+		TorListeners   TorListenersConfig `yaml:"tor-listeners"`
546
+		WebSockets     struct {
544 547
 			AllowedOrigins       []string `yaml:"allowed-origins"`
545 548
 			allowedOriginRegexps []*regexp.Regexp
546 549
 		}
@@ -845,13 +848,30 @@ func (conf *Config) Operators(oc map[string]*OperClass) (map[string]*Oper, error
845 848
 	return operators, nil
846 849
 }
847 850
 
848
-func loadTlsConfig(config TLSListenConfig, webSocket bool) (tlsConfig *tls.Config, err error) {
849
-	cert, err := tls.LoadX509KeyPair(config.Cert, config.Key)
850
-	if err != nil {
851
-		return nil, &CertKeyError{Err: err}
851
+func loadTlsConfig(config listenerConfigBlock) (tlsConfig *tls.Config, err error) {
852
+	var certificates []tls.Certificate
853
+	if len(config.TLSCertificates) != 0 {
854
+		// SNI configuration with multiple certificates
855
+		for _, certPairConf := range config.TLSCertificates {
856
+			cert, err := loadCertWithLeaf(certPairConf.Cert, certPairConf.Key)
857
+			if err != nil {
858
+				return nil, err
859
+			}
860
+			certificates = append(certificates, cert)
861
+		}
862
+	} else if config.TLS.Cert != "" {
863
+		// normal configuration with one certificate
864
+		cert, err := loadCertWithLeaf(config.TLS.Cert, config.TLS.Key)
865
+		if err != nil {
866
+			return nil, err
867
+		}
868
+		certificates = append(certificates, cert)
869
+	} else {
870
+		// plaintext!
871
+		return nil, nil
852 872
 	}
853 873
 	clientAuth := tls.RequestClientCert
854
-	if webSocket {
874
+	if config.WebSocket {
855 875
 		// if Chrome receives a server request for a client certificate
856 876
 		// on a websocket connection, it will immediately disconnect:
857 877
 		// https://bugs.chromium.org/p/chromium/issues/detail?id=329884
@@ -859,12 +879,26 @@ func loadTlsConfig(config TLSListenConfig, webSocket bool) (tlsConfig *tls.Confi
859 879
 		clientAuth = tls.NoClientCert
860 880
 	}
861 881
 	result := tls.Config{
862
-		Certificates: []tls.Certificate{cert},
882
+		Certificates: certificates,
863 883
 		ClientAuth:   clientAuth,
864 884
 	}
865 885
 	return &result, nil
866 886
 }
867 887
 
888
+func loadCertWithLeaf(certFile, keyFile string) (cert tls.Certificate, err error) {
889
+	// LoadX509KeyPair: "On successful return, Certificate.Leaf will be nil because
890
+	// the parsed form of the certificate is not retained." tls.Config:
891
+	// "Note: if there are multiple Certificates, and they don't have the
892
+	// optional field Leaf set, certificate selection will incur a significant
893
+	// per-handshake performance cost."
894
+	cert, err = tls.LoadX509KeyPair(certFile, keyFile)
895
+	if err != nil {
896
+		return
897
+	}
898
+	cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
899
+	return
900
+}
901
+
868 902
 // prepareListeners populates Config.Server.trueListeners
869 903
 func (conf *Config) prepareListeners() (err error) {
870 904
 	if len(conf.Server.Listeners) == 0 {
@@ -880,12 +914,9 @@ func (conf *Config) prepareListeners() (err error) {
880 914
 		if lconf.STSOnly && !conf.Server.STS.Enabled {
881 915
 			return fmt.Errorf("%s is configured as a STS-only listener, but STS is disabled", addr)
882 916
 		}
883
-		if block.TLS.Cert != "" {
884
-			tlsConfig, err := loadTlsConfig(block.TLS, block.WebSocket)
885
-			if err != nil {
886
-				return err
887
-			}
888
-			lconf.TLSConfig = tlsConfig
917
+		lconf.TLSConfig, err = loadTlsConfig(block)
918
+		if err != nil {
919
+			return &CertKeyError{Err: err}
889 920
 		}
890 921
 		lconf.RequireProxy = block.TLS.Proxy || block.Proxy
891 922
 		lconf.WebSocket = block.WebSocket

+ 1
- 1
irc/mysql/history.go Zobrazit soubor

@@ -395,7 +395,7 @@ func (mysql *MySQL) deleteCorrespondents(ctx context.Context, threshold int64) {
395 395
 		mysql.logError("error deleting correspondents", err)
396 396
 	} else {
397 397
 		count, err := result.RowsAffected()
398
-		if err != nil {
398
+		if !mysql.logError("error deleting correspondents", err) {
399 399
 			mysql.logger.Debug(fmt.Sprintf("deleted %d correspondents entries", count))
400 400
 		}
401 401
 	}

+ 12
- 3
irc/nickserv.go Zobrazit soubor

@@ -817,14 +817,21 @@ func nsInfoHandler(service *ircService, server *Server, client *Client, command
817 817
 	for _, nick := range account.AdditionalNicks {
818 818
 		service.Notice(rb, fmt.Sprintf(client.t("Additional grouped nick: %s"), nick))
819 819
 	}
820
-	for _, channel := range server.accounts.ChannelsForAccount(accountName) {
821
-		service.Notice(rb, fmt.Sprintf(client.t("Registered channel: %s"), channel))
822
-	}
820
+	listRegisteredChannels(service, accountName, rb)
823 821
 	if account.Suspended != nil {
824 822
 		service.Notice(rb, suspensionToString(client, *account.Suspended))
825 823
 	}
826 824
 }
827 825
 
826
+func listRegisteredChannels(service *ircService, accountName string, rb *ResponseBuffer) {
827
+	client := rb.session.client
828
+	channels := client.server.accounts.ChannelsForAccount(accountName)
829
+	service.Notice(rb, fmt.Sprintf(client.t("You have %d registered channel(s)."), len(channels)))
830
+	for _, channel := range rb.session.client.server.accounts.ChannelsForAccount(accountName) {
831
+		service.Notice(rb, fmt.Sprintf(client.t("Registered channel: %s"), channel))
832
+	}
833
+}
834
+
828 835
 func nsRegisterHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
829 836
 	details := client.Details()
830 837
 	passphrase := params[0]
@@ -961,6 +968,8 @@ func nsUnregisterHandler(service *ircService, server *Server, client *Client, co
961 968
 			service.Notice(rb, ircfmt.Unescape(client.t("$bWarning: erasing this account will allow it to be re-registered; consider UNREGISTER instead.$b")))
962 969
 		} else {
963 970
 			service.Notice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this account will remove its stored privileges.$b")))
971
+			service.Notice(rb, ircfmt.Unescape(client.t("$bNote that an unregistered account name remains reserved and cannot be re-registered.$b")))
972
+			service.Notice(rb, ircfmt.Unescape(client.t("$bIf you are having problems with your account, contact an administrator.$b")))
964 973
 		}
965 974
 		service.Notice(rb, fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/NS %s %s %s", strings.ToUpper(command), accountName, expectedCode)))
966 975
 		return

+ 61
- 10
irc/uban.go Zobrazit soubor

@@ -13,6 +13,7 @@ import (
13 13
 
14 14
 	"github.com/oragono/oragono/irc/custime"
15 15
 	"github.com/oragono/oragono/irc/flatip"
16
+	"github.com/oragono/oragono/irc/sno"
16 17
 	"github.com/oragono/oragono/irc/utils"
17 18
 )
18 19
 
@@ -160,18 +161,64 @@ func ubanAddHandler(client *Client, target ubanTarget, params []string, rb *Resp
160 161
 
161 162
 	switch target.banType {
162 163
 	case ubanCIDR:
163
-		ubanAddCIDR(client, target, duration, requireSASL, operReason, rb)
164
+		err = ubanAddCIDR(client, target, duration, requireSASL, operReason, rb)
164 165
 	case ubanNickmask:
165
-		ubanAddNickmask(client, target, duration, operReason, rb)
166
+		err = ubanAddNickmask(client, target, duration, operReason, rb)
166 167
 	case ubanNick:
167
-		ubanAddAccount(client, target, duration, operReason, rb)
168
-
168
+		err = ubanAddAccount(client, target, duration, operReason, rb)
169
+	}
170
+	if err == nil {
171
+		announceUban(client, true, target, duration, requireSASL, operReason)
169 172
 	}
170 173
 	return false
171 174
 }
172 175
 
173
-func ubanAddCIDR(client *Client, target ubanTarget, duration time.Duration, requireSASL bool, operReason string, rb *ResponseBuffer) {
174
-	err := client.server.dlines.AddNetwork(target.cidr, duration, requireSASL, "", operReason, client.Oper().Name)
176
+func announceUban(client *Client, add bool, target ubanTarget, duration time.Duration, requireSASL bool, operReason string) {
177
+	oper := client.Oper()
178
+	if oper == nil {
179
+		return
180
+	}
181
+	operName := oper.Name
182
+
183
+	var buf strings.Builder
184
+	fmt.Fprintf(&buf, "Operator %s", operName)
185
+
186
+	if add {
187
+		buf.WriteString(" added")
188
+	} else {
189
+		buf.WriteString(" removed")
190
+	}
191
+	switch target.banType {
192
+	case ubanCIDR:
193
+		buf.WriteString(" an IP-based")
194
+	case ubanNickmask:
195
+		buf.WriteString(" a NUH-mask")
196
+	case ubanNick:
197
+		buf.WriteString(" an account suspension")
198
+	}
199
+	buf.WriteString(" UBAN against ")
200
+	switch target.banType {
201
+	case ubanCIDR:
202
+		buf.WriteString(target.cidr.String())
203
+	case ubanNickmask, ubanNick:
204
+		buf.WriteString(target.nickOrMask)
205
+	}
206
+	if duration != 0 {
207
+		fmt.Fprintf(&buf, " [duration: %v]", duration)
208
+	}
209
+	if requireSASL {
210
+		buf.WriteString(" [require-SASL]")
211
+	}
212
+	if operReason != "" {
213
+		fmt.Fprintf(&buf, " [reason: %s]", operReason)
214
+	}
215
+	line := buf.String()
216
+	client.server.snomasks.Send(sno.LocalXline, line)
217
+	client.server.logger.Info("opers", line)
218
+}
219
+
220
+func ubanAddCIDR(client *Client, target ubanTarget, duration time.Duration, requireSASL bool, operReason string, rb *ResponseBuffer) (err error) {
221
+	err = client.server.dlines.AddNetwork(target.cidr, duration, requireSASL, "", operReason, client.Oper().Name)
175 222
 	if err == nil {
176 223
 		rb.Notice(fmt.Sprintf(client.t("Successfully added UBAN for %s"), target.cidr.HumanReadableString()))
177 224
 	} else {
@@ -192,10 +239,11 @@ func ubanAddCIDR(client *Client, target ubanTarget, duration time.Duration, requ
192 239
 			rb.Notice(line)
193 240
 		}
194 241
 	}
242
+	return
195 243
 }
196 244
 
197
-func ubanAddNickmask(client *Client, target ubanTarget, duration time.Duration, operReason string, rb *ResponseBuffer) {
198
-	err := client.server.klines.AddMask(target.nickOrMask, duration, "", operReason, client.Oper().Name)
245
+func ubanAddNickmask(client *Client, target ubanTarget, duration time.Duration, operReason string, rb *ResponseBuffer) (err error) {
246
+	err = client.server.klines.AddMask(target.nickOrMask, duration, "", operReason, client.Oper().Name)
199 247
 	if err == nil {
200 248
 		rb.Notice(fmt.Sprintf(client.t("Successfully added UBAN for %s"), target.nickOrMask))
201 249
 	} else {
@@ -229,9 +277,10 @@ func ubanAddNickmask(client *Client, target ubanTarget, duration time.Duration,
229 277
 		}
230 278
 		rb.Notice(client.t("You can suspend their accounts instead; try /UBAN ADD <nickname>"))
231 279
 	}
280
+	return
232 281
 }
233 282
 
234
-func ubanAddAccount(client *Client, target ubanTarget, duration time.Duration, operReason string, rb *ResponseBuffer) {
283
+func ubanAddAccount(client *Client, target ubanTarget, duration time.Duration, operReason string, rb *ResponseBuffer) (err error) {
235 284
 	account := target.nickOrMask
236 285
 	// TODO this doesn't enumerate all sessions if ForceNickEqualsAccount is disabled
237 286
 	var sessionData []SessionData
@@ -239,7 +288,7 @@ func ubanAddAccount(client *Client, target ubanTarget, duration time.Duration, o
239 288
 		sessionData, _ = mcl.AllSessionData(nil, true)
240 289
 	}
241 290
 
242
-	err := client.server.accounts.Suspend(account, duration, client.Oper().Name, operReason)
291
+	err = client.server.accounts.Suspend(account, duration, client.Oper().Name, operReason)
243 292
 	switch err {
244 293
 	case nil:
245 294
 		rb.Notice(fmt.Sprintf(client.t("Successfully suspended account %s"), account))
@@ -254,6 +303,7 @@ func ubanAddAccount(client *Client, target ubanTarget, duration time.Duration, o
254 303
 	default:
255 304
 		rb.Notice(client.t("An error occurred"))
256 305
 	}
306
+	return
257 307
 }
258 308
 
259 309
 func ubanDelHandler(client *Client, target ubanTarget, params []string, rb *ResponseBuffer) bool {
@@ -276,6 +326,7 @@ func ubanDelHandler(client *Client, target ubanTarget, params []string, rb *Resp
276 326
 	}
277 327
 	if err == nil {
278 328
 		rb.Notice(fmt.Sprintf(client.t("Successfully removed ban on %s"), targetString))
329
+		announceUban(client, false, target, 0, false, "")
279 330
 	} else {
280 331
 		rb.Notice(fmt.Sprintf(client.t("Could not remove ban: %v"), err))
281 332
 	}

+ 2
- 0
traditional.yaml Zobrazit soubor

@@ -23,6 +23,8 @@ server:
23 23
 
24 24
         # The standard SSL/TLS port for IRC is 6697. This will listen on all interfaces:
25 25
         ":6697":
26
+            # this is a standard TLS configuration with a single certificate;
27
+            # see the manual for instructions on how to configure SNI
26 28
             tls:
27 29
                 cert: fullchain.pem
28 30
                 key: privkey.pem

Načítá se…
Zrušit
Uložit