-
Notifications
You must be signed in to change notification settings - Fork 6
Implement support for whitelists, default-deny/allow #1
Conversation
1 similar comment
1 similar comment
1 similar comment
README.md
Outdated
|
||
// the default for a filter is accept, but we can change that | ||
f.Remove(ipnet) | ||
f.filterDefault = true |
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.
Should be an exported field, otherwise it's useless.
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.
Also, I'd prefer something more descriptive like AcceptByDefault
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.
Done.
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.
Hmm you forgot to update line 44 here with the new name.
filter.go
Outdated
filters map[string]*net.IPNet | ||
mu sync.RWMutex | ||
filterDefault bool | ||
filters []*FilterEntry |
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.
What happens when there are to entries for the same IPNet? Seems behaviour now depends on how search is implemented.
Why not using `map[string]*FilterEntry ? Also simplifies searching and deleting.
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.
Ah, if this is about ipfs/kubo#1972 (comment) disregard this comment.
filter.go
Outdated
@@ -8,21 +8,34 @@ import ( | |||
manet "github.com/multiformats/go-multiaddr-net" | |||
) | |||
|
|||
type FilterEntry struct { |
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.
should be unexported as it seems to have no use externally.
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.
Can you also make sure to include godoc/golint comments in the functions you are modifying. I know they were not there before, but it's good to take the chance and improve that.
1 similar comment
This should be ready for boats now. |
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.
Hi
sorry to go back to it, can you explain why using a slice of filters is better than keeping the map that was used before?
I read too quickly the yesteday and now I don't see the point of keeping several rules for the same IPNet if only one of them is going to matter.
Thanks!!
README.md
Outdated
|
||
// the default for a filter is accept, but we can change that | ||
f.Remove(ipnet) | ||
f.filterDefault = true |
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.
Hmm you forgot to update line 44 here with the new name.
filter.go
Outdated
} | ||
|
||
// AddrBlocked parses a ma.Multiaddr, and if it can get a valid netip |
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.
and, if it can get a valid net.IPNet back, applies the Filters,...
(I'm no native English speaker so I'm not 100% sure but there's something weird with the commas in the original formulation).
filter.go
Outdated
} | ||
|
||
// AddrBlocked parses a ma.Multiaddr, and if it can get a valid netip | ||
// back applies the Filters, returning true if the given address | ||
// should be rejected, and false if the given address is allowed. |
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.
Now I'm going around reading grammar explanations about commas. It's probably wrong to use a comma before and
in this particular case.
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.
You are correct. I missplaced those commas.
@@ -34,18 +34,90 @@ func TestFilter(t *testing.T) { | |||
} | |||
} | |||
|
|||
for _, notBlocked := range []string{ | |||
// test that other net intervals are not blocked |
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.
Can you split this into multiple test functions, one for each exported method?
} | ||
|
||
f.mu.RLock() | ||
defer f.mu.RUnlock() | ||
|
||
var reject bool = f.RejectByDefault |
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.
Above there is a comment saying that the last policy added to the filter is authoritative
.
AddDialFilter
and AddAllowFilter
append policies to the filter slice.
And here there is a for loop which looks for a matching rule from the beginning of the slice, so actually the first policy added to the filter is authoritative?. Also, see my comment above about going back to using a map.
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.
"By here I mean 2 lines below"
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.
Right. So we set the default result (f.RejectByDefault
), then iterate through all filters. The filters slice is in insertion order, so the first filter inserted is applied first. We apply all filters to the address, and if the filter matches the address we change the reject
outcome to be the outcome specified by the matching rule.
To be specific about the expected behavior:
DEFAULT DENY
ALLOW 192.168.0.0/16
DENY 192.160.0.0/24
ALLOW 192.168.0.5/32
These rules specify to reject all connections, allowing a /16
, blocking a /24
out of that, and and making exception for a single /32
. These filters work because they're insertion ordered. This how other ACL systems tend to operate with the last rule to match winning. Better ACL systems make some attempt to sort, compact & optimize the ACL table, but this is usually Good Enough.
So as an exercise, given an attempted connection to 192.168.0.5
, we will first take the default outcome which is DENY
. Then we apply the first rule, which matches and says ALLOW
presumably because that /16
is trusted address space. But there may be other relevant policies so we have to keep going. We apply the next rule, which also matches and produces a DENY
. But another rule could express an exception so we keep going, and we find that the particular /32
is ALLOW
ed explicitly.
We can't go back to a map because maps aren't insertion ordered. The unordered map system works fine when you just want to see if there's a reject rule which matches and there's no possibility of countermanding a reject decision once one has been made. However because the map has no concept of ordering it's impossible to impose a hierarchy between rules allowing for exceptions as in this example. It's also not possible to achieve this sort of behavior with two maps (which was the second thing I tried) because again the ordering is ambiguous as to whether the ALLOW
or DENY
directive should win given even a simple conflict.
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.
Yeah you're right. Just saw you're not breaking out of the loop :S. Sorry.
filter.go
Outdated
// Filters returns the list of net.IPNet structs in the given filter | ||
// rules. | ||
// | ||
// FIXME: This function doesn't really make any sense now that filters |
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.
Right, can you change this to return only the reject filters as originally? Behaviour of this library should not be different than before.
Adding an AcceptFilters()
which returns the others would not hurt.
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.
Sure. The semantics of the two don't really make a ton of sense anymore since the filters are order dependent and taking either list independently looses all the semantics but sure.
1 similar comment
@whyrusleeping can you have a look here? LGTM, but note the comments on
|
Is there any update on this? I would like to use these filters. |
@arrdem Do you still have interest in this PR? |
@anacrolix My understanding was that, as a result of other IPFS-ey conversations rather than supporting whitelists of hosts with whom to peer or blacklists from whom to drop traffic which it was the intent of this PR to enable, other plans were being toyed with for private networks based on different bootstrap servers. Personally I'm not using IPFS atm, and no longer have a burning need for this feature, but I'd be happy to see it merged. That said looks like there are conflicts, this needs revisiting and frankly my next moth is a mess so really this is a project direction question for you. |
@libp2p/go-team are there any chances/interest to rescue this? |
@hsanjuan if we still want this, i can review/rebase/merge this tomorrow! |
Whitelist is really a must... Please implement this |
This makes ipfs/kubo#1972 possible by allowing the
Filter
to implement last-matching-term-wins policies. This allows for default-deny policies with exceptions or default-allow policies with large deny masks and selective permission in the same style as SQUID and other ACL systems.