Skip to content
This repository has been archived by the owner on May 14, 2020. It is now read-only.

Implement support for whitelists, default-deny/allow #1

Closed
wants to merge 14 commits into from

Conversation

arrdem
Copy link
Contributor

@arrdem arrdem commented Mar 30, 2017

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.

@coveralls
Copy link

Coverage Status

Coverage decreased (-1.7%) to 52.83% when pulling 2a8da79 on arrdem:master into 90aacb5 on libp2p:master.

1 similar comment
@coveralls
Copy link

Coverage Status

Coverage decreased (-1.7%) to 52.83% when pulling 2a8da79 on arrdem:master into 90aacb5 on libp2p:master.

@coveralls
Copy link

Coverage Status

Coverage decreased (-1.7%) to 52.83% when pulling 0bf6dc0 on arrdem:master into 90aacb5 on libp2p:master.

1 similar comment
@coveralls
Copy link

Coverage Status

Coverage decreased (-1.7%) to 52.83% when pulling 0bf6dc0 on arrdem:master into 90aacb5 on libp2p:master.

@coveralls
Copy link

coveralls commented Mar 30, 2017

Coverage Status

Coverage increased (+7.7%) to 62.264% when pulling 26ab0b9 on arrdem:master into 90aacb5 on libp2p:master.

@coveralls
Copy link

Coverage Status

Coverage decreased (-4.5%) to 50.0% when pulling c14e093 on arrdem:master into 90aacb5 on libp2p:master.

1 similar comment
@coveralls
Copy link

Coverage Status

Coverage decreased (-4.5%) to 50.0% when pulling c14e093 on arrdem:master into 90aacb5 on libp2p:master.

README.md Outdated

// the default for a filter is accept, but we can change that
f.Remove(ipnet)
f.filterDefault = true
Copy link
Contributor

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.

Copy link
Contributor

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor

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
Copy link
Contributor

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.

Copy link
Contributor

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 {
Copy link
Contributor

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.

Copy link
Contributor

@hsanjuan hsanjuan left a 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.

@coveralls
Copy link

coveralls commented Mar 30, 2017

Coverage Status

Coverage decreased (-3.8%) to 50.769% when pulling 4c905c2 on arrdem:master into 90aacb5 on libp2p:master.

@coveralls
Copy link

Coverage Status

Coverage increased (+21.3%) to 75.806% when pulling 95de304 on arrdem:master into 90aacb5 on libp2p:master.

1 similar comment
@coveralls
Copy link

Coverage Status

Coverage increased (+21.3%) to 75.806% when pulling 95de304 on arrdem:master into 90aacb5 on libp2p:master.

@arrdem
Copy link
Contributor Author

arrdem commented Mar 31, 2017

This should be ready for boats now.

Copy link
Contributor

@hsanjuan hsanjuan left a 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
Copy link
Contributor

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
Copy link
Contributor

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.
Copy link
Contributor

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.

Copy link
Contributor Author

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
Copy link
Contributor

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
Copy link
Contributor

@hsanjuan hsanjuan Mar 31, 2017

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.

Copy link
Contributor

@hsanjuan hsanjuan Mar 31, 2017

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"

Copy link
Contributor Author

@arrdem arrdem Mar 31, 2017

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 ALLOWed 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.

Copy link
Contributor

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
Copy link
Contributor

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.

Copy link
Contributor Author

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.

@coveralls
Copy link

Coverage Status

Coverage increased (+14.1%) to 68.675% when pulling a191744 on arrdem:master into 90aacb5 on libp2p:master.

1 similar comment
@coveralls
Copy link

Coverage Status

Coverage increased (+14.1%) to 68.675% when pulling a191744 on arrdem:master into 90aacb5 on libp2p:master.

@hsanjuan
Copy link
Contributor

hsanjuan commented Apr 3, 2017

@whyrusleeping can you have a look here?

LGTM, but note the comments on Filters():

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.

@tvi
Copy link

tvi commented Jul 26, 2017

Is there any update on this? I would like to use these filters.

@anacrolix
Copy link
Contributor

@arrdem Do you still have interest in this PR?

@arrdem
Copy link
Contributor Author

arrdem commented Dec 24, 2018

@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.

@hsanjuan
Copy link
Contributor

@libp2p/go-team are there any chances/interest to rescue this?

@bigs
Copy link
Contributor

bigs commented Jan 7, 2019

@hsanjuan if we still want this, i can review/rebase/merge this tomorrow!

@nodegin
Copy link

nodegin commented Feb 3, 2019

Whitelist is really a must... Please implement this

@anacrolix
Copy link
Contributor

This is now tracked in #8. @nodegin

@anacrolix anacrolix closed this Feb 3, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants