-
Notifications
You must be signed in to change notification settings - Fork 750
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
IP scrubber #2990
IP scrubber #2990
Changes from 5 commits
73cc1fe
449f53b
0f3dd97
8a0179b
6e71825
56ecfa5
89cc995
f3aef01
3d47811
7a030e3
5020713
9e19dd8
0f3c0ee
c48c25a
0858cf3
a6c4e18
82214f0
cd709cc
1332bd0
83fbd9d
a40290f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -103,6 +103,11 @@ func GetAccount(ctx context.Context, cfg *config.Configuration, fetcher stored_r | |
return nil, errs | ||
} | ||
|
||
errs = account.IpMasking.Validate(errs) | ||
if len(errs) > 0 { | ||
return nil, errs | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpick: Could simplify the syntax...
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These lines show as having test coverage, but there doesn't seem to an assertion on setting their default values. I made a type locally where There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this is tested in a general GetAccount test.
For the simplified syntax I added new variables for errors to show explicitly we don't want to add IP config validation errors to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And also tested this update end to end including test with publisher account id. |
||
|
||
// set the value of events.enabled field based on deprecated events_enabled field and ensure backward compatibility | ||
deprecateEventsEnabledField(account) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,11 @@ const ( | |
ChannelWeb ChannelType = "web" | ||
) | ||
|
||
const ( | ||
Ipv4Bits = 32 | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Ipv6Bits = 128 | ||
) | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// Account represents a publisher account configuration | ||
type Account struct { | ||
ID string `mapstructure:"id" json:"id"` | ||
|
@@ -41,6 +46,7 @@ type Account struct { | |
DefaultBidLimit int `mapstructure:"default_bid_limit" json:"default_bid_limit"` | ||
BidAdjustments *openrtb_ext.ExtRequestPrebidBidAdjustments `mapstructure:"bidadjustments" json:"bidadjustments"` | ||
Privacy *AccountPrivacy `mapstructure:"privacy" json:"privacy"` | ||
IpMasking IpMasking `mapstructure:"ip_masking" json:"ip_masking"` | ||
} | ||
|
||
// CookieSync represents the account-level defaults for the cookie sync endpoint. | ||
|
@@ -297,3 +303,36 @@ func (a *AccountChannel) IsSet() bool { | |
type AccountPrivacy struct { | ||
AllowActivities AllowActivities `mapstructure:"allowactivities" json:"allowactivities"` | ||
} | ||
|
||
type IpMasking struct { | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
IpV6 IpMasks `mapstructure:"ipv6" json:"ipv6"` | ||
IpV4 IpMasks `mapstructure:"ipv4" json:"ipv4"` | ||
} | ||
|
||
type IpMasks struct { | ||
ActivityLeftMaskBits int `mapstructure:"activity_left_mask_bits" json:"activity_left_mask_bits"` | ||
GdprLeftMaskBitsLowest int `mapstructure:"gdpr_left_mask_bits_lowest" json:"gdpr_left_mask_bits_lowest"` | ||
GdprLeftMaskBitsHighest int `mapstructure:"gdpr_left_mask_bits_highest" json:"gdpr_left_mask_bits_highest"` | ||
} | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpick: Let's use underscores to be consistent with the rest of this object: |
||
|
||
func (ipMasking *IpMasking) Validate(errs []error) []error { | ||
errs = ipMasking.IpV6.validate(errs, Ipv6Bits, "ipv6") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You need to handle the case of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Technically There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, please. Let's add some defensive coding since |
||
errs = ipMasking.IpV4.validate(errs, Ipv4Bits, "ipv4") | ||
return errs | ||
} | ||
|
||
func (ipMasks *IpMasks) validate(errs []error, maxBits int, ipVersion string) []error { | ||
if ipMasks.ActivityLeftMaskBits > maxBits || ipMasks.ActivityLeftMaskBits < 0 { | ||
err := fmt.Errorf("activity left mask bits cannot exceed %d in %s address, or be less than 0", maxBits, ipVersion) | ||
errs = append(errs, err) | ||
} | ||
if ipMasks.GdprLeftMaskBitsLowest > maxBits || ipMasks.GdprLeftMaskBitsLowest < 0 { | ||
err := fmt.Errorf("gdpr left mask bits lowest cannot exceed %d in %s address, or be less than 0", maxBits, ipVersion) | ||
errs = append(errs, err) | ||
} | ||
if ipMasks.GdprLeftMaskBitsHighest > maxBits || ipMasks.GdprLeftMaskBitsHighest < 0 { | ||
err := fmt.Errorf("gdpr left mask bits highest cannot exceed %d in %s address, or be less than 0", maxBits, ipVersion) | ||
errs = append(errs, err) | ||
} | ||
return errs | ||
} | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,8 +2,9 @@ package privacy | |
|
||
import ( | ||
"encoding/json" | ||
"github.com/prebid/prebid-server/config" | ||
"github.com/prebid/prebid-server/util/ptrutil" | ||
"strings" | ||
"net" | ||
|
||
"github.com/prebid/openrtb/v19/openrtb2" | ||
) | ||
|
@@ -76,14 +77,18 @@ type Scrubber interface { | |
ScrubUser(user *openrtb2.User, strategy ScrubStrategyUser, geo ScrubStrategyGeo) *openrtb2.User | ||
} | ||
|
||
type scrubber struct{} | ||
type scrubber struct { | ||
ipMasking *config.IpMasking | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this need to be passed as a pointer? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My main idea here was to load these values to a non-pointer config struct, because we always need them. And then pass it as a pointer downstream to avoid copying the struct. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That is't a surefire premature optimization. Go has optimizations for passing arguments as values which may be much more efficient than using a pointer. It's a bit more reliable for large data structures like the BidRequest, but this struct is relatively small. |
||
} | ||
|
||
// NewScrubber returns an OpenRTB scrubber. | ||
func NewScrubber() Scrubber { | ||
return scrubber{} | ||
func NewScrubber(ipMasking *config.IpMasking) Scrubber { | ||
return &scrubber{ | ||
ipMasking: ipMasking, | ||
} | ||
} | ||
|
||
func (scrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforcement) *openrtb2.BidRequest { | ||
func (s *scrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforcement) *openrtb2.BidRequest { | ||
var userExtParsed map[string]json.RawMessage | ||
userExtModified := false | ||
|
||
|
@@ -166,8 +171,8 @@ func (scrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforc | |
if deviceCopy.Geo != nil { | ||
deviceCopy.Geo = scrubGeoPrecision(deviceCopy.Geo) | ||
} | ||
deviceCopy.IP = scrubIPV4Lowest8(deviceCopy.IP) | ||
deviceCopy.IPv6 = scrubIPV6Lowest32Bits(deviceCopy.IPv6) | ||
deviceCopy.IP = scrubIp(deviceCopy.IP, s.ipMasking.IpV4.GdprLeftMaskBitsLowest, config.Ipv4Bits) | ||
deviceCopy.IPv6 = scrubIp(deviceCopy.IPv6, s.ipMasking.IpV6.ActivityLeftMaskBits, config.Ipv6Bits) | ||
} | ||
} | ||
|
||
|
@@ -176,7 +181,7 @@ func (scrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforc | |
return bidRequest | ||
} | ||
|
||
func (scrubber) ScrubDevice(device *openrtb2.Device, id ScrubStrategyDeviceID, ipv4 ScrubStrategyIPV4, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb2.Device { | ||
func (s *scrubber) ScrubDevice(device *openrtb2.Device, id ScrubStrategyDeviceID, ipv4 ScrubStrategyIPV4, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb2.Device { | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if device == nil { | ||
return nil | ||
} | ||
|
@@ -196,14 +201,14 @@ func (scrubber) ScrubDevice(device *openrtb2.Device, id ScrubStrategyDeviceID, i | |
|
||
switch ipv4 { | ||
case ScrubStrategyIPV4Lowest8: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we still need There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need this to indicate we want to scrub IPv4. There are just 2 options: const (
// ScrubStrategyIPV4None does not remove any part of an IPV4 address.
ScrubStrategyIPV4None ScrubStrategyIPV4 = iota
// ScrubStrategyIPV4Lowest8 zeroes out the last 8 bits of an IPV4 address.
ScrubStrategyIPV4Lowest8
) I think we should rename it to something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah ok. Yes, renaming would be good. Perhaps There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right! |
||
deviceCopy.IP = scrubIPV4Lowest8(device.IP) | ||
deviceCopy.IP = scrubIp(device.IP, s.ipMasking.IpV4.GdprLeftMaskBitsLowest, config.Ipv4Bits) | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
switch ipv6 { | ||
case ScrubStrategyIPV6Lowest16: | ||
deviceCopy.IPv6 = scrubIPV6Lowest16Bits(device.IPv6) | ||
deviceCopy.IPv6 = scrubIp(device.IPv6, s.ipMasking.IpV6.GdprLeftMaskBitsLowest, config.Ipv6Bits) | ||
case ScrubStrategyIPV6Lowest32: | ||
deviceCopy.IPv6 = scrubIPV6Lowest32Bits(device.IPv6) | ||
deviceCopy.IPv6 = scrubIp(device.IPv6, s.ipMasking.IpV6.GdprLeftMaskBitsHighest, config.Ipv6Bits) | ||
} | ||
|
||
switch geo { | ||
|
@@ -241,44 +246,13 @@ func (scrubber) ScrubUser(user *openrtb2.User, strategy ScrubStrategyUser, geo S | |
return &userCopy | ||
} | ||
|
||
func scrubIPV4Lowest8(ip string) string { | ||
i := strings.LastIndex(ip, ".") | ||
if i == -1 { | ||
func scrubIp(ip string, ones, bits int) string { | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if ip == "" { | ||
return "" | ||
} | ||
|
||
return ip[0:i] + ".0" | ||
} | ||
|
||
func scrubIPV6Lowest16Bits(ip string) string { | ||
ip = removeLowestIPV6Segment(ip) | ||
|
||
if ip != "" { | ||
ip += ":0" | ||
} | ||
|
||
return ip | ||
} | ||
|
||
func scrubIPV6Lowest32Bits(ip string) string { | ||
ip = removeLowestIPV6Segment(ip) | ||
ip = removeLowestIPV6Segment(ip) | ||
|
||
if ip != "" { | ||
ip += ":0:0" | ||
} | ||
|
||
return ip | ||
} | ||
|
||
func removeLowestIPV6Segment(ip string) string { | ||
i := strings.LastIndex(ip, ":") | ||
|
||
if i == -1 { | ||
return "" | ||
} | ||
|
||
return ip[0:i] | ||
ipv6Mask := net.CIDRMask(ones, bits) | ||
ipMasked := net.ParseIP(ip).Mask(ipv6Mask) | ||
return ipMasked.String() | ||
} | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
func scrubGeoFull(geo *openrtb2.Geo) *openrtb2.Geo { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reasonable way to add a test that throws this error? Or is it challenging with this Validate function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, added!