diff --git a/irc/cloaking/cloak.go b/irc/cloaking/cloak.go index 9247884f..9de8e967 100644 --- a/irc/cloaking/cloak.go +++ b/irc/cloaking/cloak.go @@ -6,7 +6,9 @@ package cloak import ( "crypto/hmac" + "crypto/rand" "crypto/sha256" + "encoding/base64" "errors" "fmt" "net" @@ -28,7 +30,7 @@ var ( errNetName = errors.New("NetName is not the right size (must be 1-10 characters long)") errNotIPv4 = errors.New("The given address is not an IPv4 address") errConfigDisabled = errors.New("Config has disabled IP cloaking") - errKeysTooShort = fmt.Errorf("Cloaking keys too short (min: %d)", MinKeyLength) + errKeysTooShort = errors.New("Cloaking keys too short") errKeysNotRandomEnough = errors.New("Cloaking keys aren't random enough") ) @@ -38,22 +40,63 @@ type Config struct { Enabled bool // NetName is the name used for the network in cloaked addresses. NetName string - // IPv4KeyAString is used to cloak the `a` part of the IP address. - IPv4KeyAString string `yaml:"ipv4-key-a"` + // IPv4KeyString is used to cloak the `a`, `b`, `c` and `d` parts of the IP address. + // it is split up into the separate A/B/C/D keys below. + IPv4KeysString []string `yaml:"ipv4-keys"` IPv4KeyA []byte - // IPv4KeyBString is used to cloak the `b` part of the IP address. - IPv4KeyBString string `yaml:"ipv4-key-b"` IPv4KeyB []byte - // IPv4KeyCString is used to cloak the `c` part of the IP address. - IPv4KeyCString string `yaml:"ipv4-key-c"` IPv4KeyC []byte - // IPv4KeyDString is used to cloak the `d` part of the IP address. - IPv4KeyDString string `yaml:"ipv4-key-d"` IPv4KeyD []byte } +// CheckConfig checks whether we're configured correctly. +func (config *Config) CheckConfig() error { + if config.Enabled { + // IPv4 cloak keys + if len(config.IPv4KeysString) < 4 { + return errKeysTooShort + } + + keyA, errA := base64.StdEncoding.DecodeString(config.IPv4KeysString[0]) + keyB, errB := base64.StdEncoding.DecodeString(config.IPv4KeysString[1]) + keyC, errC := base64.StdEncoding.DecodeString(config.IPv4KeysString[2]) + keyD, errD := base64.StdEncoding.DecodeString(config.IPv4KeysString[3]) + + if errA != nil || errB != nil || errC != nil || errD != nil { + return fmt.Errorf("Could not decode IPv4 cloak keys") + } + if len(keyA) < MinKeyLength || len(keyB) < MinKeyLength || len(keyC) < MinKeyLength || len(keyD) < MinKeyLength { + return errKeysTooShort + } + + config.IPv4KeyA = keyA + config.IPv4KeyB = keyB + config.IPv4KeyC = keyC + config.IPv4KeyD = keyD + + // try cloaking IPs to confirm everything works properly + _, err := IPv4(net.ParseIP("8.8.8.8"), config) + if err != nil { + return err + } + } + return nil +} + +// GenerateCloakKey generates one cloak key. +func GenerateCloakKey() (string, error) { + keyBytes := make([]byte, MinKeyLength) + _, err := rand.Read(keyBytes) + if err != nil { + return "", fmt.Errorf("Could not generate random bytes for cloak key: %s", err.Error()) + } + + return base64.StdEncoding.EncodeToString(keyBytes), nil +} + // IsRandomEnough makes sure people are using keys that are random enough. func IsRandomEnough(key []byte) bool { + //TODO(dan): actually find out how to calc this return true } @@ -85,7 +128,7 @@ func hashOctet(key []byte, data string) string { // if you have 4.5.6.7 and 4.3.2.1 then the `a` part of those addresses will // be the same value. This ensures chanops can properly ban dodgy people as // they need to do so. -func IPv4(address net.IP, config Config) (string, error) { +func IPv4(address net.IP, config *Config) (string, error) { if !config.Enabled { return "", errConfigDisabled } diff --git a/irc/config.go b/irc/config.go index 89a85fca..8f297606 100644 --- a/irc/config.go +++ b/irc/config.go @@ -11,7 +11,6 @@ import ( "fmt" "io/ioutil" "log" - "net" "strings" "time" @@ -421,11 +420,9 @@ func LoadConfig(filename string) (config *Config, err error) { return nil, fmt.Errorf("STS port is incorrect, should be 0 if disabled: %d", config.Server.STS.Port) } } - if config.Network.IPCloaking.Enabled { - _, err := cloak.IPv4(net.ParseIP("8.8.8.8"), config.Network.IPCloaking) - if err != nil { - return nil, fmt.Errorf("IPv4 cloaking config is incorrect: %s", err.Error()) - } + err = config.Network.IPCloaking.CheckConfig() + if err != nil { + return nil, fmt.Errorf("Could not parse IP cloaking: %s", err.Error()) } if config.Server.ConnectionThrottle.Enabled { config.Server.ConnectionThrottle.Duration, err = time.ParseDuration(config.Server.ConnectionThrottle.DurationString) diff --git a/oragono.go b/oragono.go index 172decaf..4c0839af 100644 --- a/oragono.go +++ b/oragono.go @@ -13,10 +13,6 @@ import ( "syscall" "time" - "net" - - "encoding/base64" - "github.com/docopt/docopt-go" "github.com/oragono/oragono/irc" cloak "github.com/oragono/oragono/irc/cloaking" @@ -33,6 +29,7 @@ Usage: oragono initdb [--conf ] [--quiet] oragono upgradedb [--conf ] [--quiet] oragono genpasswd [--conf ] [--quiet] + oragono genkeys oragono mkcerts [--conf ] [--quiet] oragono run [--conf ] [--quiet] oragono -h | --help @@ -43,57 +40,37 @@ Options: -h --help Show this screen. --version Show version.` - keyA, _ := base64.StdEncoding.DecodeString("idXACDbEhqRZsExn0jOTi4rtC6MrKBOcN4edxdSzTAA=") - keyB, _ := base64.StdEncoding.DecodeString("qODtg8WEJ0YA6JRnryDDUEoSdJyrGgPFI6hPNnGHyIw=") - keyC, _ := base64.StdEncoding.DecodeString("Oxqc6uDsyEO5vZcxHmtZ1zOLL8wwATeYA4KqJmkTJQo=") - keyD, _ := base64.StdEncoding.DecodeString("vd2eimWWh3L9fukFwxZThJ9pKTf/I5UZ/k7o/3JHkMc=") - - conf := cloak.Config{ - Enabled: true, - NetName: "Test", - IPv4KeyA: keyA, - IPv4KeyB: keyB, - IPv4KeyC: keyC, - IPv4KeyD: keyD, - } - ip := net.ParseIP("8.8.8.8") - key, err := cloak.IPv4(ip, conf) - fmt.Println(ip, key, err) - ip = net.ParseIP("9.4.8.8") - key, err = cloak.IPv4(ip, conf) - fmt.Println(ip, key, err) - ip = net.ParseIP("8.4.2.8") - key, err = cloak.IPv4(ip, conf) - fmt.Println(ip, key, err) - ip = net.ParseIP("8.4.2.1") - key, err = cloak.IPv4(ip, conf) - fmt.Println(ip, key, err) - arguments, _ := docopt.Parse(usage, nil, true, version, false) + // load config and logger for everything but genkeys + var err error configfile := arguments["--conf"].(string) - config, err := irc.LoadConfig(configfile) - if err != nil { - log.Fatal("Config file did not load successfully:", err.Error()) - } + var config *irc.Config + var logman *logger.Manager + if !arguments["genkeys"].(bool) { + config, err = irc.LoadConfig(configfile) + if err != nil { + log.Fatal("Config file did not load successfully:", err.Error()) + } - // assemble separate log configs - var logConfigs []logger.Config - for _, lConfig := range config.Logging { - logConfigs = append(logConfigs, logger.Config{ - MethodStdout: lConfig.MethodStdout, - MethodStderr: lConfig.MethodStderr, - MethodFile: lConfig.MethodFile, - Filename: lConfig.Filename, - Level: lConfig.Level, - Types: lConfig.Types, - ExcludedTypes: lConfig.ExcludedTypes, - }) - } + // assemble separate log configs + var logConfigs []logger.Config + for _, lConfig := range config.Logging { + logConfigs = append(logConfigs, logger.Config{ + MethodStdout: lConfig.MethodStdout, + MethodStderr: lConfig.MethodStderr, + MethodFile: lConfig.MethodFile, + Filename: lConfig.Filename, + Level: lConfig.Level, + Types: lConfig.Types, + ExcludedTypes: lConfig.ExcludedTypes, + }) + } - logger, err := logger.NewManager(logConfigs...) - if err != nil { - log.Fatal("Logger did not load successfully:", err.Error()) + logman, err = logger.NewManager(logConfigs...) + if err != nil { + log.Fatal("Logger did not load successfully:", err.Error()) + } } if arguments["genpasswd"].(bool) { @@ -109,6 +86,65 @@ Options: } fmt.Print("\n") fmt.Println(encoded) + } else if arguments["genkeys"].(bool) { + fmt.Println("Here are your cloak keys:") + + // generate IPv4 keys + keyA, err := cloak.GenerateCloakKey() + if err != nil { + log.Fatal("Error generating cloak keys:", err) + } + keyB, err := cloak.GenerateCloakKey() + if err != nil { + log.Fatal("Error generating cloak keys:", err) + } + keyC, err := cloak.GenerateCloakKey() + if err != nil { + log.Fatal("Error generating cloak keys:", err) + } + keyD, err := cloak.GenerateCloakKey() + if err != nil { + log.Fatal("Error generating cloak keys:", err) + } + + fmt.Println(fmt.Sprintf(`ipv4-keys: ["%s", "%s", "%s", "%s"]`, keyA, keyB, keyC, keyD)) + + // generate IPv6 keys + keyA, err = cloak.GenerateCloakKey() + if err != nil { + log.Fatal("Error generating cloak keys:", err) + } + keyB, err = cloak.GenerateCloakKey() + if err != nil { + log.Fatal("Error generating cloak keys:", err) + } + keyC, err = cloak.GenerateCloakKey() + if err != nil { + log.Fatal("Error generating cloak keys:", err) + } + keyD, err = cloak.GenerateCloakKey() + if err != nil { + log.Fatal("Error generating cloak keys:", err) + } + keyE, err := cloak.GenerateCloakKey() + if err != nil { + log.Fatal("Error generating cloak keys:", err) + } + keyF, err := cloak.GenerateCloakKey() + if err != nil { + log.Fatal("Error generating cloak keys:", err) + } + keyG, err := cloak.GenerateCloakKey() + if err != nil { + log.Fatal("Error generating cloak keys:", err) + } + keyH, err := cloak.GenerateCloakKey() + if err != nil { + log.Fatal("Error generating cloak keys:", err) + } + + fmt.Println(fmt.Sprintf(`ipv6-keys: ["%s", "%s", "%s", "%s", "%s", "%s", "%s", "%s"]`, keyA, keyB, keyC, keyD, keyE, keyF, keyG, keyH)) + } else if arguments["initdb"].(bool) { irc.InitDB(config.Datastore.Path) if !arguments["--quiet"].(bool) { @@ -139,13 +175,13 @@ Options: } else if arguments["run"].(bool) { rand.Seed(time.Now().UTC().UnixNano()) if !arguments["--quiet"].(bool) { - logger.Info("startup", fmt.Sprintf("Oragono v%s starting", irc.SemVer)) + logman.Info("startup", fmt.Sprintf("Oragono v%s starting", irc.SemVer)) } // profiling if config.Debug.StackImpact.Enabled { if config.Debug.StackImpact.AgentKey == "" || config.Debug.StackImpact.AppName == "" { - logger.Error("startup", "Could not start StackImpact - agent-key or app-name are undefined") + logman.Error("startup", "Could not start StackImpact - agent-key or app-name are undefined") return } @@ -153,22 +189,22 @@ Options: agent.Start(stackimpact.Options{AgentKey: config.Debug.StackImpact.AgentKey, AppName: config.Debug.StackImpact.AppName}) defer agent.RecordPanic() - logger.Info("startup", fmt.Sprintf("StackImpact profiling started as %s", config.Debug.StackImpact.AppName)) + logman.Info("startup", fmt.Sprintf("StackImpact profiling started as %s", config.Debug.StackImpact.AppName)) } // warning if running a non-final version if strings.Contains(irc.SemVer, "unreleased") { - logger.Warning("startup", "You are currently running an unreleased beta version of Oragono that may be unstable and could corrupt your database.\nIf you are running a production network, please download the latest build from https://oragono.io/downloads.html and run that instead.") + logman.Warning("startup", "You are currently running an unreleased beta version of Oragono that may be unstable and could corrupt your database.\nIf you are running a production network, please download the latest build from https://oragono.io/downloads.html and run that instead.") } - server, err := irc.NewServer(configfile, config, logger) + server, err := irc.NewServer(configfile, config, logman) if err != nil { - logger.Error("startup", fmt.Sprintf("Could not load server: %s", err.Error())) + logman.Error("startup", fmt.Sprintf("Could not load server: %s", err.Error())) return } if !arguments["--quiet"].(bool) { - logger.Info("startup", "Server running") - defer logger.Info("shutdown", fmt.Sprintf("Oragono v%s exiting", irc.SemVer)) + logman.Info("startup", "Server running") + defer logman.Info("shutdown", fmt.Sprintf("Oragono v%s exiting", irc.SemVer)) } server.Run() } diff --git a/oragono.yaml b/oragono.yaml index bfb961a8..af026ae4 100644 --- a/oragono.yaml +++ b/oragono.yaml @@ -5,6 +5,26 @@ network: # name of the network name: OragonoTest + # cloaking IP addresses and hostnames + ip-cloaking: + # enable the cloaking + enabled: false + + # short name to use in cloaked hostnames + netname: "testnet" + + # ipv4 cloak keys + # to generate these keys, run "oragono genkeys" + ipv4-keys: ["keyhere", "keyhere", "keyhere", "keyhere"] + + # ipv6 cloak keys + # to generate these keys, run "oragono genkeys" + ipv6-keys: ["keyhere", "keyhere", "keyhere", "keyhere", "keyhere", "keyhere", "keyhere", "keyhere"] + + # hostname cloaking keys + # to generate these keys, run "oragono genkeys" + hostname-keys: ["keyhere", "keyhere", "keyhere", "keyhere"] + # server configuration server: # server name