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
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
# you may want to use your network name here
netname: "irc"

View File

@ -297,6 +297,12 @@ server:
# whether to enable IP cloaking
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
# you may want to use your network name here
netname: "irc"

View File

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

View File

@ -104,3 +104,25 @@ func BenchmarkCloaks(b *testing.B) {
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 {
Enabled bool
Netname string
CidrLenIPv4 int `yaml:"cidr-len-ipv4"`
CidrLenIPv6 int `yaml:"cidr-len-ipv6"`
NumBits int `yaml:"num-bits"`
LegacySecretValue string `yaml:"secret"`
Enabled bool
EnabledForAlwaysOn bool `yaml:"enabled-for-always-on"`
Netname string
CidrLenIPv4 int `yaml:"cidr-len-ipv4"`
CidrLenIPv6 int `yaml:"cidr-len-ipv6"`
NumBits int `yaml:"num-bits"`
LegacySecretValue string `yaml:"secret"`
secret string
numBytes int
@ -26,14 +27,10 @@ type CloakConfig struct {
}
func (cloakConfig *CloakConfig) Initialize() {
if !cloakConfig.Enabled {
return
}
// sanity checks:
numBits := cloakConfig.NumBits
if 0 == numBits {
numBits = 80
numBits = 64
} else if 256 < numBits {
numBits = 256
}
@ -69,12 +66,30 @@ func (config *CloakConfig) ComputeCloak(ip net.IP) string {
} else {
masked = ip.Mask(config.ipv6Mask)
}
return config.macAndCompose(masked)
}
func (config *CloakConfig) macAndCompose(b []byte) string {
// SHA3(K || M):
// 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[len(config.secret):], masked)
copy(input[len(config.secret):], b)
digest := sha3.Sum512(input)
b32digest := utils.B32Encoder.EncodeToString(digest[:config.numBytes])
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
// XXX this modifies config after the initial load, which is naughty,
// 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
server.SetConfig(config)