Loading pkg/core/hashring.go +33 −0 Original line number Diff line number Diff line Loading @@ -5,9 +5,13 @@ package core import ( "crypto/hmac" "crypto/sha256" "encoding/binary" "encoding/json" "errors" "fmt" "hash" "hash/crc64" "log" "sort" Loading Loading @@ -41,6 +45,35 @@ type ResourceState struct { Notworking []Resource `json:"not_working"` } // Rootkey represents the hmac root key created from the rdsys secret key // and used to derive distributor keys for each rdsys distributor type RootKey hash.Hash // BucketKey is the hmac key derived from RootKey for a specific distributor type BucketKey struct { bucketKey hash.Hash } func NewRootKey(secretKey string) RootKey { rootKey := []byte(secretKey) return hmac.New(sha256.New, rootKey) } func NewBucketKey(secret string, distName string) BucketKey { rootKey := NewRootKey(secret) rootKey.Write([]byte(distName)) bucketKey := rootKey.Sum(nil) return BucketKey{ bucketKey: hmac.New(sha256.New, bucketKey), } } func (b *BucketKey) NewBucketHash(usrBytes []byte) Hashkey { b.bucketKey.Write(usrBytes) h := b.bucketKey.Sum(nil) return Hashkey(binary.BigEndian.Uint64(h[:8])) } // Hashkey represents an index in a hashring. type Hashkey uint64 Loading pkg/usecases/distributors/common/time.go +4 −4 Original line number Diff line number Diff line Loading @@ -88,7 +88,7 @@ func (td *TimeDistribution) housekeeping(rStream chan *core.ResourceDiff) { } } func (td *TimeDistribution) GetBridges(tpe string, ip net.IP, req *core.BridgeRequest) []string { func (td *TimeDistribution) GetBridges(tpe string, ip net.IP, req *core.BridgeRequest, bucketKey core.BucketKey) []string { hashring := td.collection.GetHashring(td.getProportionIndex(), tpe) var resources []core.Resource Loading @@ -96,7 +96,7 @@ func (td *TimeDistribution) GetBridges(tpe string, ip net.IP, req *core.BridgeRe resources = hashring.GetAll() } else { var err error resources, err = hashring.GetMany(IpHashkey(ip), td.Cfg.NumBridgesPerRequest, req) resources, err = hashring.GetMany(IpHashkey(ip, bucketKey), td.Cfg.NumBridgesPerRequest, req) if err != nil { log.Println("Error getting resources from the subhashring:", err) } Loading Loading @@ -126,10 +126,10 @@ func (td *TimeDistribution) getProportionIndex() string { return strconv.Itoa(period % td.Cfg.NumPeriods) } func IpHashkey(ip net.IP) core.Hashkey { func IpHashkey(ip net.IP, bucketKey core.BucketKey) core.Hashkey { mask := net.CIDRMask(32, 128) if ip.To4() != nil { mask = net.CIDRMask(16, 32) } return core.NewHashkey(ip.Mask(mask).String()) return bucketKey.NewBucketHash([]byte(ip.Mask(mask))) } pkg/usecases/distributors/email/email.go +4 −1 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ type EmailDistributor struct { ipc delivery.Mechanism wg sync.WaitGroup shutdown chan bool bucketKey core.BucketKey } type Command struct { Loading @@ -59,6 +60,8 @@ func (d *EmailDistributor) Init(cfg *internal.Config) { d.cfg = &cfg.Distributors.Email d.shutdown = make(chan bool) d.bucketKey = core.NewBucketKey(cfg.Backend.SecretHashKey, DistName) collectionConfig := core.CollectionConfig{} for _, rType := range d.cfg.Resources { collectionConfig.Types = append(collectionConfig.Types, core.TypeConfig{ Loading Loading @@ -123,7 +126,7 @@ func (d *EmailDistributor) GetResources(address string, command *Command) []core now := time.Now().Unix() / (60 * 60) period := now / int64(d.cfg.RotationPeriodHours) hashKey := core.NewHashkey(fmt.Sprintf("%s-%d", address, period)) hashKey := d.bucketKey.NewBucketHash([]byte(fmt.Sprintf("%s-%d", address, period))) ipVersion := core.IPv4 if command.IPv6 { Loading pkg/usecases/distributors/https/https.go +3 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ const ( // HttpsDistributor contains all the context that the distributor needs to run. type HttpsDistributor struct { timeDistribution *common.TimeDistribution bucketKey core.BucketKey cfg *internal.Config } Loading Loading @@ -60,7 +61,7 @@ func (d *HttpsDistributor) RequestBridges(opts RequestBridgesOptions) ([]string, if opts.IPv6 { req.IPVersion = core.IPv6 } r := d.timeDistribution.GetBridges(opts.Type, opts.IP, req) r := d.timeDistribution.GetBridges(opts.Type, opts.IP, req, d.bucketKey) return r, nil } Loading @@ -69,6 +70,7 @@ func (d *HttpsDistributor) Init(cfg *internal.Config) { log.Printf("Initialising %s distributor.", DistName) d.cfg = cfg d.bucketKey = core.NewBucketKey(cfg.Backend.SecretHashKey, DistName) log.Printf("Initialising resource stream.") d.timeDistribution = &common.TimeDistribution{ ResourceStreamURL: cfg.Backend.ResourceStreamURL(), Loading pkg/usecases/distributors/moat/moat.go +4 −2 Original line number Diff line number Diff line Loading @@ -64,6 +64,7 @@ type MoatDistributor struct { cfg *internal.MoatDistConfig wg sync.WaitGroup shutdown chan bool bucketKey core.BucketKey // FetchBridges gets the list of builtin bridgelines from a remote url // the bridgeLines map is indexed by bridge type Loading Loading @@ -153,7 +154,7 @@ func (d *MoatDistributor) getBridges(bs BridgeSettings, ip net.IP, shimToken, co return bridges[bs.Type] case "bridgedb": return d.timeDistribution.GetBridges(bs.Type, ip, req) return d.timeDistribution.GetBridges(bs.Type, ip, req, d.bucketKey) default: log.Println("Requested an unsuported bridge source:", bs.Source) Loading @@ -178,7 +179,7 @@ func (d *MoatDistributor) GetBridges(transport string, ip net.IP, country string IPVersion: core.IPv4, Country: country, } return d.timeDistribution.GetBridges(transport, ip, req) return d.timeDistribution.GetBridges(transport, ip, req, d.bucketKey) } func (d *MoatDistributor) GetBuiltInBridges(types []string) map[string][]string { Loading Loading @@ -239,6 +240,7 @@ func (d *MoatDistributor) Init(cfg *internal.Config) { d.shutdown = make(chan bool) d.builtinBridges = make(map[string][]string) d.fetchBuiltinBridges() d.bucketKey = core.NewBucketKey(cfg.Backend.SecretHashKey, DistName) d.timeDistribution = &common.TimeDistribution{ ResourceStreamURL: cfg.Backend.ResourceStreamURL(), Loading Loading
pkg/core/hashring.go +33 −0 Original line number Diff line number Diff line Loading @@ -5,9 +5,13 @@ package core import ( "crypto/hmac" "crypto/sha256" "encoding/binary" "encoding/json" "errors" "fmt" "hash" "hash/crc64" "log" "sort" Loading Loading @@ -41,6 +45,35 @@ type ResourceState struct { Notworking []Resource `json:"not_working"` } // Rootkey represents the hmac root key created from the rdsys secret key // and used to derive distributor keys for each rdsys distributor type RootKey hash.Hash // BucketKey is the hmac key derived from RootKey for a specific distributor type BucketKey struct { bucketKey hash.Hash } func NewRootKey(secretKey string) RootKey { rootKey := []byte(secretKey) return hmac.New(sha256.New, rootKey) } func NewBucketKey(secret string, distName string) BucketKey { rootKey := NewRootKey(secret) rootKey.Write([]byte(distName)) bucketKey := rootKey.Sum(nil) return BucketKey{ bucketKey: hmac.New(sha256.New, bucketKey), } } func (b *BucketKey) NewBucketHash(usrBytes []byte) Hashkey { b.bucketKey.Write(usrBytes) h := b.bucketKey.Sum(nil) return Hashkey(binary.BigEndian.Uint64(h[:8])) } // Hashkey represents an index in a hashring. type Hashkey uint64 Loading
pkg/usecases/distributors/common/time.go +4 −4 Original line number Diff line number Diff line Loading @@ -88,7 +88,7 @@ func (td *TimeDistribution) housekeeping(rStream chan *core.ResourceDiff) { } } func (td *TimeDistribution) GetBridges(tpe string, ip net.IP, req *core.BridgeRequest) []string { func (td *TimeDistribution) GetBridges(tpe string, ip net.IP, req *core.BridgeRequest, bucketKey core.BucketKey) []string { hashring := td.collection.GetHashring(td.getProportionIndex(), tpe) var resources []core.Resource Loading @@ -96,7 +96,7 @@ func (td *TimeDistribution) GetBridges(tpe string, ip net.IP, req *core.BridgeRe resources = hashring.GetAll() } else { var err error resources, err = hashring.GetMany(IpHashkey(ip), td.Cfg.NumBridgesPerRequest, req) resources, err = hashring.GetMany(IpHashkey(ip, bucketKey), td.Cfg.NumBridgesPerRequest, req) if err != nil { log.Println("Error getting resources from the subhashring:", err) } Loading Loading @@ -126,10 +126,10 @@ func (td *TimeDistribution) getProportionIndex() string { return strconv.Itoa(period % td.Cfg.NumPeriods) } func IpHashkey(ip net.IP) core.Hashkey { func IpHashkey(ip net.IP, bucketKey core.BucketKey) core.Hashkey { mask := net.CIDRMask(32, 128) if ip.To4() != nil { mask = net.CIDRMask(16, 32) } return core.NewHashkey(ip.Mask(mask).String()) return bucketKey.NewBucketHash([]byte(ip.Mask(mask))) }
pkg/usecases/distributors/email/email.go +4 −1 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ type EmailDistributor struct { ipc delivery.Mechanism wg sync.WaitGroup shutdown chan bool bucketKey core.BucketKey } type Command struct { Loading @@ -59,6 +60,8 @@ func (d *EmailDistributor) Init(cfg *internal.Config) { d.cfg = &cfg.Distributors.Email d.shutdown = make(chan bool) d.bucketKey = core.NewBucketKey(cfg.Backend.SecretHashKey, DistName) collectionConfig := core.CollectionConfig{} for _, rType := range d.cfg.Resources { collectionConfig.Types = append(collectionConfig.Types, core.TypeConfig{ Loading Loading @@ -123,7 +126,7 @@ func (d *EmailDistributor) GetResources(address string, command *Command) []core now := time.Now().Unix() / (60 * 60) period := now / int64(d.cfg.RotationPeriodHours) hashKey := core.NewHashkey(fmt.Sprintf("%s-%d", address, period)) hashKey := d.bucketKey.NewBucketHash([]byte(fmt.Sprintf("%s-%d", address, period))) ipVersion := core.IPv4 if command.IPv6 { Loading
pkg/usecases/distributors/https/https.go +3 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ const ( // HttpsDistributor contains all the context that the distributor needs to run. type HttpsDistributor struct { timeDistribution *common.TimeDistribution bucketKey core.BucketKey cfg *internal.Config } Loading Loading @@ -60,7 +61,7 @@ func (d *HttpsDistributor) RequestBridges(opts RequestBridgesOptions) ([]string, if opts.IPv6 { req.IPVersion = core.IPv6 } r := d.timeDistribution.GetBridges(opts.Type, opts.IP, req) r := d.timeDistribution.GetBridges(opts.Type, opts.IP, req, d.bucketKey) return r, nil } Loading @@ -69,6 +70,7 @@ func (d *HttpsDistributor) Init(cfg *internal.Config) { log.Printf("Initialising %s distributor.", DistName) d.cfg = cfg d.bucketKey = core.NewBucketKey(cfg.Backend.SecretHashKey, DistName) log.Printf("Initialising resource stream.") d.timeDistribution = &common.TimeDistribution{ ResourceStreamURL: cfg.Backend.ResourceStreamURL(), Loading
pkg/usecases/distributors/moat/moat.go +4 −2 Original line number Diff line number Diff line Loading @@ -64,6 +64,7 @@ type MoatDistributor struct { cfg *internal.MoatDistConfig wg sync.WaitGroup shutdown chan bool bucketKey core.BucketKey // FetchBridges gets the list of builtin bridgelines from a remote url // the bridgeLines map is indexed by bridge type Loading Loading @@ -153,7 +154,7 @@ func (d *MoatDistributor) getBridges(bs BridgeSettings, ip net.IP, shimToken, co return bridges[bs.Type] case "bridgedb": return d.timeDistribution.GetBridges(bs.Type, ip, req) return d.timeDistribution.GetBridges(bs.Type, ip, req, d.bucketKey) default: log.Println("Requested an unsuported bridge source:", bs.Source) Loading @@ -178,7 +179,7 @@ func (d *MoatDistributor) GetBridges(transport string, ip net.IP, country string IPVersion: core.IPv4, Country: country, } return d.timeDistribution.GetBridges(transport, ip, req) return d.timeDistribution.GetBridges(transport, ip, req, d.bucketKey) } func (d *MoatDistributor) GetBuiltInBridges(types []string) map[string][]string { Loading Loading @@ -239,6 +240,7 @@ func (d *MoatDistributor) Init(cfg *internal.Config) { d.shutdown = make(chan bool) d.builtinBridges = make(map[string][]string) d.fetchBuiltinBridges() d.bucketKey = core.NewBucketKey(cfg.Backend.SecretHashKey, DistName) d.timeDistribution = &common.TimeDistribution{ ResourceStreamURL: cfg.Backend.ResourceStreamURL(), Loading