Merge pull request #1313 from slingamn/issue1312_unique_hostnames.1

fix #1312
This commit is contained in:
Shivaram Lingamneni 2020-10-08 15:42:55 -07:00 committed by GitHub
commit 3cd0e40146
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 69 additions and 18 deletions

View File

@ -269,6 +269,12 @@ server:
# whether to enable IP cloaking # whether to enable IP cloaking
enabled: false enabled: false
# whether to use these cloak settings (specifically, `netname` and `num-bits`)
# to produce unique hostnames for always-on clients. you can enable this even if
# you disabled IP cloaking for normal clients above. if this is disabled,
# always-on clients will all have an identical hostname (the server name).
enabled-for-always-on: true
# fake TLD at the end of the hostname, e.g., pwbs2ui4377257x8.irc # fake TLD at the end of the hostname, e.g., pwbs2ui4377257x8.irc
# you may want to use your network name here # you may want to use your network name here
netname: "irc" netname: "irc"

View File

@ -297,6 +297,12 @@ server:
# whether to enable IP cloaking # whether to enable IP cloaking
enabled: true enabled: true
# whether to use these cloak settings (specifically, `netname` and `num-bits`)
# to produce unique hostnames for always-on clients. you can enable this even if
# you disabled IP cloaking for normal clients above. if this is disabled,
# always-on clients will all have an identical hostname (the server name).
enabled-for-always-on: true
# fake TLD at the end of the hostname, e.g., pwbs2ui4377257x8.irc # fake TLD at the end of the hostname, e.g., pwbs2ui4377257x8.irc
# you may want to use your network name here # you may want to use your network name here
netname: "irc" netname: "irc"

View File

@ -411,6 +411,11 @@ func (server *Server) AddAlwaysOnClient(account ClientAccount, chnames []string,
lastSeen = map[string]time.Time{"": now} lastSeen = map[string]time.Time{"": now}
} }
hostname := server.name
if config.Server.Cloaks.EnabledForAlwaysOn {
hostname = config.Server.Cloaks.ComputeAccountCloak(account.Name)
}
client := &Client{ client := &Client{
lastSeen: lastSeen, lastSeen: lastSeen,
lastActive: now, lastActive: now,
@ -419,9 +424,8 @@ func (server *Server) AddAlwaysOnClient(account ClientAccount, chnames []string,
languages: server.Languages().Default(), languages: server.Languages().Default(),
server: server, server: server,
// TODO figure out how to set these on reattach?
username: "~user", username: "~user",
rawHostname: server.name, rawHostname: hostname,
realIP: utils.IPv4LoopbackAddress, realIP: utils.IPv4LoopbackAddress,
alwaysOn: true, alwaysOn: true,

View File

@ -104,3 +104,25 @@ func BenchmarkCloaks(b *testing.B) {
config.ComputeCloak(v6ip) config.ComputeCloak(v6ip)
} }
} }
func TestAccountCloak(t *testing.T) {
config := cloakConfForTesting()
// just assert that we get all distinct values
assertEqual(config.ComputeAccountCloak("shivaram"), "8yu8kunudb45ztxm.oragono", t)
assertEqual(config.ComputeAccountCloak("dolph🐬n"), "hhgeqsvzeagv3wjw.oragono", t)
assertEqual(config.ComputeAccountCloak("SHIVARAM"), "bgx32x4r7qzih4uh.oragono", t)
assertEqual(config.ComputeAccountCloak("ed"), "j5autmgxtdjdyzf4.oragono", t)
}
func TestAccountCloakCollisions(t *testing.T) {
config := cloakConfForTesting()
v4ip := easyParseIP("97.97.97.97")
v4cloak := config.ComputeCloak(v4ip)
// "aaaa" is the same bytestring as 97.97.97.97
aaaacloak := config.ComputeAccountCloak("aaaa")
if v4cloak == aaaacloak {
t.Errorf("cloak collision between 97.97.97.97 and aaaa: %s", v4cloak)
}
}

View File

@ -12,12 +12,13 @@ import (
) )
type CloakConfig struct { type CloakConfig struct {
Enabled bool Enabled bool
Netname string EnabledForAlwaysOn bool `yaml:"enabled-for-always-on"`
CidrLenIPv4 int `yaml:"cidr-len-ipv4"` Netname string
CidrLenIPv6 int `yaml:"cidr-len-ipv6"` CidrLenIPv4 int `yaml:"cidr-len-ipv4"`
NumBits int `yaml:"num-bits"` CidrLenIPv6 int `yaml:"cidr-len-ipv6"`
LegacySecretValue string `yaml:"secret"` NumBits int `yaml:"num-bits"`
LegacySecretValue string `yaml:"secret"`
secret string secret string
numBytes int numBytes int
@ -26,14 +27,10 @@ type CloakConfig struct {
} }
func (cloakConfig *CloakConfig) Initialize() { func (cloakConfig *CloakConfig) Initialize() {
if !cloakConfig.Enabled {
return
}
// sanity checks: // sanity checks:
numBits := cloakConfig.NumBits numBits := cloakConfig.NumBits
if 0 == numBits { if 0 == numBits {
numBits = 80 numBits = 64
} else if 256 < numBits { } else if 256 < numBits {
numBits = 256 numBits = 256
} }
@ -69,12 +66,30 @@ func (config *CloakConfig) ComputeCloak(ip net.IP) string {
} else { } else {
masked = ip.Mask(config.ipv6Mask) masked = ip.Mask(config.ipv6Mask)
} }
return config.macAndCompose(masked)
}
func (config *CloakConfig) macAndCompose(b []byte) string {
// SHA3(K || M): // SHA3(K || M):
// https://crypto.stackexchange.com/questions/17735/is-hmac-needed-for-a-sha-3-based-mac // https://crypto.stackexchange.com/questions/17735/is-hmac-needed-for-a-sha-3-based-mac
input := make([]byte, len(config.secret)+len(masked)) input := make([]byte, len(config.secret)+len(b))
copy(input, config.secret[:]) copy(input, config.secret[:])
copy(input[len(config.secret):], masked) copy(input[len(config.secret):], b)
digest := sha3.Sum512(input) digest := sha3.Sum512(input)
b32digest := utils.B32Encoder.EncodeToString(digest[:config.numBytes]) b32digest := utils.B32Encoder.EncodeToString(digest[:config.numBytes])
return fmt.Sprintf("%s.%s", b32digest, config.Netname) return fmt.Sprintf("%s.%s", b32digest, config.Netname)
} }
func (config *CloakConfig) ComputeAccountCloak(accountName string) string {
// XXX don't bother checking EnabledForAlwaysOn, since if it's disabled,
// we need to use the server name which we don't have
if config.NumBits == 0 || config.secret == "" {
return config.Netname
}
// pad with 16 initial bytes of zeroes, avoiding any possibility of collision
// with a masked IP that could be an input to ComputeCloak:
paddedAccountName := make([]byte, 16+len(accountName))
copy(paddedAccountName[16:], accountName[:])
return config.macAndCompose(paddedAccountName)
}

View File

@ -599,9 +599,7 @@ func (server *Server) applyConfig(config *Config) (err error) {
// now that the datastore is initialized, we can load the cloak secret from it // now that the datastore is initialized, we can load the cloak secret from it
// XXX this modifies config after the initial load, which is naughty, // XXX this modifies config after the initial load, which is naughty,
// but there's no data race because we haven't done SetConfig yet // but there's no data race because we haven't done SetConfig yet
if config.Server.Cloaks.Enabled { config.Server.Cloaks.SetSecret(LoadCloakSecret(server.store))
config.Server.Cloaks.SetSecret(LoadCloakSecret(server.store))
}
// activate the new config // activate the new config
server.SetConfig(config) server.SetConfig(config)