Browse Source

fix #952

tags/v2.1.0-rc1
Shivaram Lingamneni 4 years ago
parent
commit
8c74b0660b
9 changed files with 113 additions and 63 deletions
  1. 0
    11
      conventional.yaml
  2. 3
    3
      irc/cloaks/cloak_test.go
  3. 18
    17
      irc/cloaks/cloaks.go
  4. 0
    3
      irc/config.go
  5. 35
    1
      irc/database.go
  6. 28
    0
      irc/hostserv.go
  7. 28
    11
      irc/server.go
  8. 1
    6
      oragono.go
  9. 0
    11
      oragono.yaml

+ 0
- 11
conventional.yaml View File

@@ -233,17 +233,6 @@ server:
233 233
         # fake TLD at the end of the hostname, e.g., pwbs2ui4377257x8.oragono
234 234
         netname: "oragono"
235 235
 
236
-        # secret key to prevent dictionary attacks against cloaked IPs
237
-        # any high-entropy secret is valid for this purpose:
238
-        # you MUST generate a new one for your installation.
239
-        # suggestion: use the output of `oragono mksecret`
240
-        # note that rotating this key will invalidate all existing ban masks.
241
-        secret: "siaELnk6Kaeo65K3RCrwJjlWaZ-Bt3WuZ2L8MXLbNb4"
242
-
243
-        # name of an environment variable to pull the secret from, for use with
244
-        # k8s secret distribution:
245
-        # secret-environment-variable: "ORAGONO_CLOAKING_SECRET"
246
-
247 236
         # the cloaked hostname is derived only from the CIDR (most significant bits
248 237
         # of the IP address), up to a configurable number of bits. this is the
249 238
         # granularity at which bans will take effect for IPv4. Note that changing

+ 3
- 3
irc/cloaks/cloak_test.go View File

@@ -27,7 +27,7 @@ func cloakConfForTesting() CloakConfig {
27 27
 	config := CloakConfig{
28 28
 		Enabled:     true,
29 29
 		Netname:     "oragono",
30
-		Secret:      "_BdVPWB5sray7McbFmeuJL996yaLgG4l9tEyficGXKg",
30
+		secret:      "_BdVPWB5sray7McbFmeuJL996yaLgG4l9tEyficGXKg",
31 31
 		CidrLenIPv4: 32,
32 32
 		CidrLenIPv6: 64,
33 33
 		NumBits:     80,
@@ -55,7 +55,7 @@ func TestCloakDeterminism(t *testing.T) {
55 55
 	assertEqual(config.ComputeCloak(v6ipdifferentcidr), "ccmptyrjwsxv4f4d.oragono", t)
56 56
 
57 57
 	// cloak values must be sensitive to changes in the secret key
58
-	config.Secret = "HJcXK4lLawxBE4-9SIdPji_21YiL3N5r5f5-SPNrGVY"
58
+	config.SetSecret("HJcXK4lLawxBE4-9SIdPji_21YiL3N5r5f5-SPNrGVY")
59 59
 	assertEqual(config.ComputeCloak(v4ip), "4khy3usk8mfu42pe.oragono", t)
60 60
 	assertEqual(config.ComputeCloak(v6mappedIP), "4khy3usk8mfu42pe.oragono", t)
61 61
 	assertEqual(config.ComputeCloak(v6ip), "mxpk3c83vdxkek9j.oragono", t)
@@ -66,7 +66,7 @@ func TestCloakShortv4Cidr(t *testing.T) {
66 66
 	config := CloakConfig{
67 67
 		Enabled:     true,
68 68
 		Netname:     "oragono",
69
-		Secret:      "_BdVPWB5sray7McbFmeuJL996yaLgG4l9tEyficGXKg",
69
+		secret:      "_BdVPWB5sray7McbFmeuJL996yaLgG4l9tEyficGXKg",
70 70
 		CidrLenIPv4: 24,
71 71
 		CidrLenIPv6: 64,
72 72
 		NumBits:     60,

+ 18
- 17
irc/cloaks/cloaks.go View File

@@ -5,7 +5,6 @@ package cloaks
5 5
 import (
6 6
 	"fmt"
7 7
 	"net"
8
-	"os"
9 8
 
10 9
 	"golang.org/x/crypto/sha3"
11 10
 
@@ -13,25 +12,22 @@ import (
13 12
 )
14 13
 
15 14
 type CloakConfig struct {
16
-	Enabled      bool
17
-	Netname      string
18
-	Secret       string
19
-	SecretEnvVar string `yaml:"secret-environment-variable"`
20
-	CidrLenIPv4  int    `yaml:"cidr-len-ipv4"`
21
-	CidrLenIPv6  int    `yaml:"cidr-len-ipv6"`
22
-	NumBits      int    `yaml:"num-bits"`
15
+	Enabled           bool
16
+	Netname           string
17
+	CidrLenIPv4       int    `yaml:"cidr-len-ipv4"`
18
+	CidrLenIPv6       int    `yaml:"cidr-len-ipv6"`
19
+	NumBits           int    `yaml:"num-bits"`
20
+	LegacySecretValue string `yaml:"secret"`
23 21
 
22
+	secret   string
24 23
 	numBytes int
25 24
 	ipv4Mask net.IPMask
26 25
 	ipv6Mask net.IPMask
27 26
 }
28 27
 
29 28
 func (cloakConfig *CloakConfig) Initialize() {
30
-	if cloakConfig.SecretEnvVar != "" {
31
-		envSecret := os.Getenv(cloakConfig.SecretEnvVar)
32
-		if envSecret != "" {
33
-			cloakConfig.Secret = envSecret
34
-		}
29
+	if !cloakConfig.Enabled {
30
+		return
35 31
 	}
36 32
 
37 33
 	// sanity checks:
@@ -52,15 +48,20 @@ func (cloakConfig *CloakConfig) Initialize() {
52 48
 	cloakConfig.ipv6Mask = net.CIDRMask(cloakConfig.CidrLenIPv6, 128)
53 49
 }
54 50
 
51
+func (cloakConfig *CloakConfig) SetSecret(secret string) {
52
+	cloakConfig.secret = secret
53
+}
54
+
55 55
 // simple cloaking algorithm: normalize the IP to its CIDR,
56 56
 // then hash the resulting bytes with a secret key,
57 57
 // then truncate to the desired length, b32encode, and append the fake TLD.
58 58
 func (config *CloakConfig) ComputeCloak(ip net.IP) string {
59 59
 	if !config.Enabled {
60 60
 		return ""
61
-	} else if config.NumBits == 0 {
61
+	} else if config.NumBits == 0 || config.secret == "" {
62 62
 		return config.Netname
63 63
 	}
64
+
64 65
 	var masked net.IP
65 66
 	v4ip := ip.To4()
66 67
 	if v4ip != nil {
@@ -70,9 +71,9 @@ func (config *CloakConfig) ComputeCloak(ip net.IP) string {
70 71
 	}
71 72
 	// SHA3(K || M):
72 73
 	// https://crypto.stackexchange.com/questions/17735/is-hmac-needed-for-a-sha-3-based-mac
73
-	input := make([]byte, len(config.Secret)+len(masked))
74
-	copy(input, config.Secret[:])
75
-	copy(input[len(config.Secret):], masked)
74
+	input := make([]byte, len(config.secret)+len(masked))
75
+	copy(input, config.secret[:])
76
+	copy(input[len(config.secret):], masked)
76 77
 	digest := sha3.Sum512(input)
77 78
 	b32digest := utils.B32Encoder.EncodeToString(digest[:config.numBytes])
78 79
 	return fmt.Sprintf("%s.%s", b32digest, config.Netname)

+ 0
- 3
irc/config.go View File

@@ -1114,9 +1114,6 @@ func LoadConfig(filename string) (config *Config, err error) {
1114 1114
 
1115 1115
 	config.Server.Cloaks.Initialize()
1116 1116
 	if config.Server.Cloaks.Enabled {
1117
-		if config.Server.Cloaks.Secret == "" || config.Server.Cloaks.Secret == "siaELnk6Kaeo65K3RCrwJjlWaZ-Bt3WuZ2L8MXLbNb4" {
1118
-			return nil, fmt.Errorf("You must generate a new value of server.ip-cloaking.secret to enable cloaking")
1119
-		}
1120 1117
 		if !utils.IsHostname(config.Server.Cloaks.Netname) {
1121 1118
 			return nil, fmt.Errorf("Invalid netname for cloaked hostnames: %s", config.Server.Cloaks.Netname)
1122 1119
 		}

+ 35
- 1
irc/database.go View File

@@ -23,7 +23,9 @@ const (
23 23
 	// 'version' of the database schema
24 24
 	keySchemaVersion = "db.version"
25 25
 	// latest schema of the db
26
-	latestDbSchema = "10"
26
+	latestDbSchema = "11"
27
+
28
+	keyCloakSecret = "crypto.cloak_secret"
27 29
 )
28 30
 
29 31
 type SchemaChanger func(*Config, *buntdb.Tx) error
@@ -63,6 +65,7 @@ func initializeDB(path string) error {
63 65
 	err = store.Update(func(tx *buntdb.Tx) error {
64 66
 		// set schema version
65 67
 		tx.Set(keySchemaVersion, latestDbSchema, nil)
68
+		tx.Set(keyCloakSecret, utils.GenerateSecretKey(), nil)
66 69
 		return nil
67 70
 	})
68 71
 
@@ -186,6 +189,21 @@ func UpgradeDB(config *Config) (err error) {
186 189
 	return err
187 190
 }
188 191
 
192
+func LoadCloakSecret(db *buntdb.DB) (result string) {
193
+	db.View(func(tx *buntdb.Tx) error {
194
+		result, _ = tx.Get(keyCloakSecret)
195
+		return nil
196
+	})
197
+	return
198
+}
199
+
200
+func StoreCloakSecret(db *buntdb.DB, secret string) {
201
+	db.Update(func(tx *buntdb.Tx) error {
202
+		tx.Set(keyCloakSecret, secret, nil)
203
+		return nil
204
+	})
205
+}
206
+
189 207
 func schemaChangeV1toV2(config *Config, tx *buntdb.Tx) error {
190 208
 	// == version 1 -> 2 ==
191 209
 	// account key changes and account.verified key bugfix.
@@ -621,6 +639,17 @@ func schemaChangeV9ToV10(config *Config, tx *buntdb.Tx) error {
621 639
 	return nil
622 640
 }
623 641
 
642
+// #952: move the cloak secret into the database,
643
+// generate a new one if necessary
644
+func schemaChangeV10ToV11(config *Config, tx *buntdb.Tx) error {
645
+	cloakSecret := config.Server.Cloaks.LegacySecretValue
646
+	if cloakSecret == "" || cloakSecret == "siaELnk6Kaeo65K3RCrwJjlWaZ-Bt3WuZ2L8MXLbNb4" {
647
+		cloakSecret = utils.GenerateSecretKey()
648
+	}
649
+	_, _, err := tx.Set(keyCloakSecret, cloakSecret, nil)
650
+	return err
651
+}
652
+
624 653
 func init() {
625 654
 	allChanges := []SchemaChange{
626 655
 		{
@@ -668,6 +697,11 @@ func init() {
668 697
 			TargetVersion:  "10",
669 698
 			Changer:        schemaChangeV9ToV10,
670 699
 		},
700
+		{
701
+			InitialVersion: "10",
702
+			TargetVersion:  "11",
703
+			Changer:        schemaChangeV10ToV11,
704
+		},
671 705
 	}
672 706
 
673 707
 	// build the index

+ 28
- 0
irc/hostserv.go View File

@@ -9,7 +9,10 @@ import (
9 9
 	"regexp"
10 10
 	"time"
11 11
 
12
+	"github.com/goshuirc/irc-go/ircfmt"
13
+
12 14
 	"github.com/oragono/oragono/irc/sno"
15
+	"github.com/oragono/oragono/irc/utils"
13 16
 )
14 17
 
15 18
 const (
@@ -171,6 +174,19 @@ the offered vhosts, use /HOSTSERV OFFERLIST.`,
171 174
 			minParams:    1,
172 175
 			maxParams:    1,
173 176
 		},
177
+		"setcloaksecret": {
178
+			handler: hsSetCloakSecretHandler,
179
+			help: `Syntax: $bSETCLOAKSECRET$b <secret> [code]
180
+
181
+SETCLOAKSECRET can be used to set or rotate the cloak secret. You should use
182
+a cryptographically strong secret. To prevent accidental modification, a
183
+verification code is required; invoking the command without a code will
184
+display the necessary code.`,
185
+			helpShort: `$bSETCLOAKSECRET$b modifies the IP cloaking secret.`,
186
+			capabs:    []string{"vhosts", "rehash"},
187
+			minParams: 1,
188
+			maxParams: 2,
189
+		},
174 190
 	}
175 191
 )
176 192
 
@@ -429,3 +445,15 @@ func hsTakeHandler(server *Server, client *Client, command string, params []stri
429 445
 		server.snomasks.Send(sno.LocalVhosts, fmt.Sprintf("Client %s (account %s) took vhost %s", client.Nick(), account, vhost))
430 446
 	}
431 447
 }
448
+
449
+func hsSetCloakSecretHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
450
+	secret := params[0]
451
+	expectedCode := utils.ConfirmationCode(secret, server.ctime)
452
+	if len(params) == 1 || params[1] != expectedCode {
453
+		hsNotice(rb, ircfmt.Unescape(client.t("$bWarning: changing the cloak secret will invalidate stored ban/invite/exception lists.$b")))
454
+		hsNotice(rb, fmt.Sprintf(client.t("To confirm, type: /HS SETCLOAKSECRET %[1]s %[2]s"), secret, expectedCode))
455
+		return
456
+	}
457
+	StoreCloakSecret(server.store, secret)
458
+	hsNotice(rb, client.t("Rotated the cloak secret; you must rehash or restart the server for it to take effect"))
459
+}

+ 28
- 11
irc/server.go View File

@@ -550,9 +550,34 @@ func (server *Server) applyConfig(config *Config) (err error) {
550 550
 		}
551 551
 	}
552 552
 
553
+	server.logger.Info("server", "Using datastore", config.Datastore.Path)
554
+	if initial {
555
+		if err := server.loadDatastore(config); err != nil {
556
+			return err
557
+		}
558
+	} else {
559
+		if config.Datastore.MySQL.Enabled && config.Datastore.MySQL != oldConfig.Datastore.MySQL {
560
+			server.historyDB.SetConfig(config.Datastore.MySQL)
561
+		}
562
+	}
563
+
564
+	// now that the datastore is initialized, we can load the cloak secret from it
565
+	// XXX this modifies config after the initial load, which is naughty,
566
+	// but there's no data race because we haven't done SetConfig yet
567
+	if config.Server.Cloaks.Enabled {
568
+		config.Server.Cloaks.SetSecret(LoadCloakSecret(server.store))
569
+	}
570
+
553 571
 	// activate the new config
554 572
 	server.SetConfig(config)
555 573
 
574
+	// load [dk]-lines, registered users and channels, etc.
575
+	if initial {
576
+		if err := server.loadFromDatastore(config); err != nil {
577
+			return err
578
+		}
579
+	}
580
+
556 581
 	// burst new and removed caps
557 582
 	addedCaps, removedCaps := config.Diff(oldConfig)
558 583
 	var capBurstSessions []*Session
@@ -582,17 +607,6 @@ func (server *Server) applyConfig(config *Config) (err error) {
582 607
 		}
583 608
 	}
584 609
 
585
-	server.logger.Info("server", "Using datastore", config.Datastore.Path)
586
-	if initial {
587
-		if err := server.loadDatastore(config); err != nil {
588
-			return err
589
-		}
590
-	} else {
591
-		if config.Datastore.MySQL.Enabled && config.Datastore.MySQL != oldConfig.Datastore.MySQL {
592
-			server.historyDB.SetConfig(config.Datastore.MySQL)
593
-		}
594
-	}
595
-
596 610
 	server.setupPprofListener(config)
597 611
 
598 612
 	// set RPL_ISUPPORT
@@ -702,10 +716,13 @@ func (server *Server) loadDatastore(config *Config) error {
702 716
 	db, err := OpenDatabase(config)
703 717
 	if err == nil {
704 718
 		server.store = db
719
+		return nil
705 720
 	} else {
706 721
 		return fmt.Errorf("Failed to open datastore: %s", err.Error())
707 722
 	}
723
+}
708 724
 
725
+func (server *Server) loadFromDatastore(config *Config) (err error) {
709 726
 	// load *lines (from the datastores)
710 727
 	server.logger.Debug("server", "Loading D/Klines")
711 728
 	server.loadDLines()

+ 1
- 6
oragono.go View File

@@ -17,7 +17,6 @@ import (
17 17
 	"github.com/oragono/oragono/irc"
18 18
 	"github.com/oragono/oragono/irc/logger"
19 19
 	"github.com/oragono/oragono/irc/mkcerts"
20
-	"github.com/oragono/oragono/irc/utils"
21 20
 	"golang.org/x/crypto/bcrypt"
22 21
 	"golang.org/x/crypto/ssh/terminal"
23 22
 )
@@ -97,7 +96,6 @@ Usage:
97 96
 	oragono upgradedb [--conf <filename>] [--quiet]
98 97
 	oragono genpasswd [--conf <filename>] [--quiet]
99 98
 	oragono mkcerts [--conf <filename>] [--quiet]
100
-	oragono mksecret [--conf <filename>] [--quiet]
101 99
 	oragono run [--conf <filename>] [--quiet] [--smoke]
102 100
 	oragono -h | --help
103 101
 	oragono --version
@@ -109,7 +107,7 @@ Options:
109 107
 
110 108
 	arguments, _ := docopt.ParseArgs(usage, nil, version)
111 109
 
112
-	// don't require a config file for genpasswd or mksecret
110
+	// don't require a config file for genpasswd
113 111
 	if arguments["genpasswd"].(bool) {
114 112
 		var password string
115 113
 		fd := int(os.Stdin.Fd())
@@ -135,9 +133,6 @@ Options:
135 133
 			fmt.Println()
136 134
 		}
137 135
 		return
138
-	} else if arguments["mksecret"].(bool) {
139
-		fmt.Println(utils.GenerateSecretKey())
140
-		return
141 136
 	} else if arguments["mkcerts"].(bool) {
142 137
 		doMkcerts(arguments["--conf"].(string), arguments["--quiet"].(bool))
143 138
 		return

+ 0
- 11
oragono.yaml View File

@@ -254,17 +254,6 @@ server:
254 254
         # fake TLD at the end of the hostname, e.g., pwbs2ui4377257x8.oragono
255 255
         netname: "oragono"
256 256
 
257
-        # secret key to prevent dictionary attacks against cloaked IPs
258
-        # any high-entropy secret is valid for this purpose:
259
-        # you MUST generate a new one for your installation.
260
-        # suggestion: use the output of `oragono mksecret`
261
-        # note that rotating this key will invalidate all existing ban masks.
262
-        secret: "siaELnk6Kaeo65K3RCrwJjlWaZ-Bt3WuZ2L8MXLbNb4"
263
-
264
-        # name of an environment variable to pull the secret from, for use with
265
-        # k8s secret distribution:
266
-        # secret-environment-variable: "ORAGONO_CLOAKING_SECRET"
267
-
268 257
         # the cloaked hostname is derived only from the CIDR (most significant bits
269 258
         # of the IP address), up to a configurable number of bits. this is the
270 259
         # granularity at which bans will take effect for IPv4. Note that changing

Loading…
Cancel
Save