Browse Source

Merge pull request #1608 from slingamn/sni.1

close remaining 2.6 issues
tags/v2.6.0-rc1
Shivaram Lingamneni 3 years ago
parent
commit
639817f014
No account linked to committer's email address
8 changed files with 156 additions and 39 deletions
  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 View File

49
 
49
 
50
         # The standard SSL/TLS port for IRC is 6697. This will listen on all interfaces:
50
         # The standard SSL/TLS port for IRC is 6697. This will listen on all interfaces:
51
         ":6697":
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
             tls:
54
             tls:
53
                 cert: fullchain.pem
55
                 cert: fullchain.pem
54
                 key: privkey.pem
56
                 key: privkey.pem

+ 15
- 0
docs/MANUAL.md View File

49
     - [Redirect from plaintext to TLS](#how-can-i-redirect-users-from-plaintext-to-tls)
49
     - [Redirect from plaintext to TLS](#how-can-i-redirect-users-from-plaintext-to-tls)
50
     - [Reverse proxies](#reverse-proxies)
50
     - [Reverse proxies](#reverse-proxies)
51
     - [Client certificates](#client-certificates)
51
     - [Client certificates](#client-certificates)
52
+    - [SNI](#sni)
52
 - [Modes](#modes)
53
 - [Modes](#modes)
53
     - [User Modes](#user-modes)
54
     - [User Modes](#user-modes)
54
     - [Channel Modes](#channel-modes)
55
     - [Channel Modes](#channel-modes)
606
 
607
 
607
 Client certificates are not supported over websockets due to a [Chrome bug](https://bugs.chromium.org/p/chromium/issues/detail?id=329884).
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 View File

79
 For example, $bAMODE #channel +o dan$b grants the holder of the "dan"
79
 For example, $bAMODE #channel +o dan$b grants the holder of the "dan"
80
 account the +o operator mode every time they join #channel. To list current
80
 account the +o operator mode every time they join #channel. To list current
81
 accounts and modes, use $bAMODE #channel$b. Note that users are always
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
 			helpShort: `$bAMODE$b modifies persistent mode settings for channel members.`,
85
 			helpShort: `$bAMODE$b modifies persistent mode settings for channel members.`,
84
 			enabled:   chanregEnabled,
86
 			enabled:   chanregEnabled,
85
 			minParams: 1,
87
 			minParams: 1,
144
 INFO displays info about a registered channel.`,
146
 INFO displays info about a registered channel.`,
145
 			helpShort: `$bINFO$b displays info about a registered channel.`,
147
 			helpShort: `$bINFO$b displays info about a registered channel.`,
146
 			enabled:   chanregEnabled,
148
 			enabled:   chanregEnabled,
147
-			minParams: 1,
148
 		},
149
 		},
149
 		"get": {
150
 		"get": {
150
 			handler: csGetHandler,
151
 			handler: csGetHandler,
743
 }
744
 }
744
 
745
 
745
 func csInfoHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
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
 	chname, err := CasefoldChannel(params[0])
753
 	chname, err := CasefoldChannel(params[0])
747
 	if err != nil {
754
 	if err != nil {
748
 		service.Notice(rb, client.t("Invalid channel name"))
755
 		service.Notice(rb, client.t("Invalid channel name"))

+ 54
- 23
irc/config.go View File

8
 import (
8
 import (
9
 	"bytes"
9
 	"bytes"
10
 	"crypto/tls"
10
 	"crypto/tls"
11
+	"crypto/x509"
11
 	"errors"
12
 	"errors"
12
 	"fmt"
13
 	"fmt"
13
 	"io"
14
 	"io"
54
 
55
 
55
 // This is the YAML-deserializable type of the value of the `Server.Listeners` map
56
 // This is the YAML-deserializable type of the value of the `Server.Listeners` map
56
 type listenerConfigBlock struct {
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
 type HistoryCutoff uint
69
 type HistoryCutoff uint
536
 		passwordBytes  []byte
540
 		passwordBytes  []byte
537
 		Name           string
541
 		Name           string
538
 		nameCasefolded string
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
 			AllowedOrigins       []string `yaml:"allowed-origins"`
547
 			AllowedOrigins       []string `yaml:"allowed-origins"`
545
 			allowedOriginRegexps []*regexp.Regexp
548
 			allowedOriginRegexps []*regexp.Regexp
546
 		}
549
 		}
845
 	return operators, nil
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
 	clientAuth := tls.RequestClientCert
873
 	clientAuth := tls.RequestClientCert
854
-	if webSocket {
874
+	if config.WebSocket {
855
 		// if Chrome receives a server request for a client certificate
875
 		// if Chrome receives a server request for a client certificate
856
 		// on a websocket connection, it will immediately disconnect:
876
 		// on a websocket connection, it will immediately disconnect:
857
 		// https://bugs.chromium.org/p/chromium/issues/detail?id=329884
877
 		// https://bugs.chromium.org/p/chromium/issues/detail?id=329884
859
 		clientAuth = tls.NoClientCert
879
 		clientAuth = tls.NoClientCert
860
 	}
880
 	}
861
 	result := tls.Config{
881
 	result := tls.Config{
862
-		Certificates: []tls.Certificate{cert},
882
+		Certificates: certificates,
863
 		ClientAuth:   clientAuth,
883
 		ClientAuth:   clientAuth,
864
 	}
884
 	}
865
 	return &result, nil
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
 // prepareListeners populates Config.Server.trueListeners
902
 // prepareListeners populates Config.Server.trueListeners
869
 func (conf *Config) prepareListeners() (err error) {
903
 func (conf *Config) prepareListeners() (err error) {
870
 	if len(conf.Server.Listeners) == 0 {
904
 	if len(conf.Server.Listeners) == 0 {
880
 		if lconf.STSOnly && !conf.Server.STS.Enabled {
914
 		if lconf.STSOnly && !conf.Server.STS.Enabled {
881
 			return fmt.Errorf("%s is configured as a STS-only listener, but STS is disabled", addr)
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
 		lconf.RequireProxy = block.TLS.Proxy || block.Proxy
921
 		lconf.RequireProxy = block.TLS.Proxy || block.Proxy
891
 		lconf.WebSocket = block.WebSocket
922
 		lconf.WebSocket = block.WebSocket

+ 1
- 1
irc/mysql/history.go View File

395
 		mysql.logError("error deleting correspondents", err)
395
 		mysql.logError("error deleting correspondents", err)
396
 	} else {
396
 	} else {
397
 		count, err := result.RowsAffected()
397
 		count, err := result.RowsAffected()
398
-		if err != nil {
398
+		if !mysql.logError("error deleting correspondents", err) {
399
 			mysql.logger.Debug(fmt.Sprintf("deleted %d correspondents entries", count))
399
 			mysql.logger.Debug(fmt.Sprintf("deleted %d correspondents entries", count))
400
 		}
400
 		}
401
 	}
401
 	}

+ 12
- 3
irc/nickserv.go View File

817
 	for _, nick := range account.AdditionalNicks {
817
 	for _, nick := range account.AdditionalNicks {
818
 		service.Notice(rb, fmt.Sprintf(client.t("Additional grouped nick: %s"), nick))
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
 	if account.Suspended != nil {
821
 	if account.Suspended != nil {
824
 		service.Notice(rb, suspensionToString(client, *account.Suspended))
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
 func nsRegisterHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
835
 func nsRegisterHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
829
 	details := client.Details()
836
 	details := client.Details()
830
 	passphrase := params[0]
837
 	passphrase := params[0]
961
 			service.Notice(rb, ircfmt.Unescape(client.t("$bWarning: erasing this account will allow it to be re-registered; consider UNREGISTER instead.$b")))
968
 			service.Notice(rb, ircfmt.Unescape(client.t("$bWarning: erasing this account will allow it to be re-registered; consider UNREGISTER instead.$b")))
962
 		} else {
969
 		} else {
963
 			service.Notice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this account will remove its stored privileges.$b")))
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
 		service.Notice(rb, fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/NS %s %s %s", strings.ToUpper(command), accountName, expectedCode)))
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
 		return
975
 		return

+ 61
- 10
irc/uban.go View File

13
 
13
 
14
 	"github.com/oragono/oragono/irc/custime"
14
 	"github.com/oragono/oragono/irc/custime"
15
 	"github.com/oragono/oragono/irc/flatip"
15
 	"github.com/oragono/oragono/irc/flatip"
16
+	"github.com/oragono/oragono/irc/sno"
16
 	"github.com/oragono/oragono/irc/utils"
17
 	"github.com/oragono/oragono/irc/utils"
17
 )
18
 )
18
 
19
 
160
 
161
 
161
 	switch target.banType {
162
 	switch target.banType {
162
 	case ubanCIDR:
163
 	case ubanCIDR:
163
-		ubanAddCIDR(client, target, duration, requireSASL, operReason, rb)
164
+		err = ubanAddCIDR(client, target, duration, requireSASL, operReason, rb)
164
 	case ubanNickmask:
165
 	case ubanNickmask:
165
-		ubanAddNickmask(client, target, duration, operReason, rb)
166
+		err = ubanAddNickmask(client, target, duration, operReason, rb)
166
 	case ubanNick:
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
 	return false
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
 	if err == nil {
222
 	if err == nil {
176
 		rb.Notice(fmt.Sprintf(client.t("Successfully added UBAN for %s"), target.cidr.HumanReadableString()))
223
 		rb.Notice(fmt.Sprintf(client.t("Successfully added UBAN for %s"), target.cidr.HumanReadableString()))
177
 	} else {
224
 	} else {
192
 			rb.Notice(line)
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
 	if err == nil {
247
 	if err == nil {
200
 		rb.Notice(fmt.Sprintf(client.t("Successfully added UBAN for %s"), target.nickOrMask))
248
 		rb.Notice(fmt.Sprintf(client.t("Successfully added UBAN for %s"), target.nickOrMask))
201
 	} else {
249
 	} else {
229
 		}
277
 		}
230
 		rb.Notice(client.t("You can suspend their accounts instead; try /UBAN ADD <nickname>"))
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
 	account := target.nickOrMask
284
 	account := target.nickOrMask
236
 	// TODO this doesn't enumerate all sessions if ForceNickEqualsAccount is disabled
285
 	// TODO this doesn't enumerate all sessions if ForceNickEqualsAccount is disabled
237
 	var sessionData []SessionData
286
 	var sessionData []SessionData
239
 		sessionData, _ = mcl.AllSessionData(nil, true)
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
 	switch err {
292
 	switch err {
244
 	case nil:
293
 	case nil:
245
 		rb.Notice(fmt.Sprintf(client.t("Successfully suspended account %s"), account))
294
 		rb.Notice(fmt.Sprintf(client.t("Successfully suspended account %s"), account))
254
 	default:
303
 	default:
255
 		rb.Notice(client.t("An error occurred"))
304
 		rb.Notice(client.t("An error occurred"))
256
 	}
305
 	}
306
+	return
257
 }
307
 }
258
 
308
 
259
 func ubanDelHandler(client *Client, target ubanTarget, params []string, rb *ResponseBuffer) bool {
309
 func ubanDelHandler(client *Client, target ubanTarget, params []string, rb *ResponseBuffer) bool {
276
 	}
326
 	}
277
 	if err == nil {
327
 	if err == nil {
278
 		rb.Notice(fmt.Sprintf(client.t("Successfully removed ban on %s"), targetString))
328
 		rb.Notice(fmt.Sprintf(client.t("Successfully removed ban on %s"), targetString))
329
+		announceUban(client, false, target, 0, false, "")
279
 	} else {
330
 	} else {
280
 		rb.Notice(fmt.Sprintf(client.t("Could not remove ban: %v"), err))
331
 		rb.Notice(fmt.Sprintf(client.t("Could not remove ban: %v"), err))
281
 	}
332
 	}

+ 2
- 0
traditional.yaml View File

23
 
23
 
24
         # The standard SSL/TLS port for IRC is 6697. This will listen on all interfaces:
24
         # The standard SSL/TLS port for IRC is 6697. This will listen on all interfaces:
25
         ":6697":
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
             tls:
28
             tls:
27
                 cert: fullchain.pem
29
                 cert: fullchain.pem
28
                 key: privkey.pem
30
                 key: privkey.pem

Loading…
Cancel
Save