Commit 65874eb0 authored by Juan Batiz-Benet's avatar Juan Batiz-Benet

Merge pull request #1378 from ipfs/feat/filter

add in basic address dial filtering
parents 3f28663f 0bf6b39c
...@@ -239,6 +239,10 @@ ...@@ -239,6 +239,10 @@
"ImportPath": "github.com/whyrusleeping/iptb", "ImportPath": "github.com/whyrusleeping/iptb",
"Rev": "3970c95a864f1a40037f796ff596607ce8ae43be" "Rev": "3970c95a864f1a40037f796ff596607ce8ae43be"
}, },
{
"ImportPath": "github.com/whyrusleeping/multiaddr-filter",
"Rev": "15837fcc356fddef27c634b0f6379b3b7f259114"
},
{ {
"ImportPath": "golang.org/x/crypto/blowfish", "ImportPath": "golang.org/x/crypto/blowfish",
"Rev": "c84e1f8e3a7e322d497cd16c0e8a13c7e127baf3" "Rev": "c84e1f8e3a7e322d497cd16c0e8a13c7e127baf3"
......
package mask
import (
"errors"
"net"
"strings"
)
func NewMask(a string) (*net.IPNet, error) {
parts := strings.Split(a, "/")
if len(parts) == 5 && parts[1] == "ip4" && parts[3] == "ipcidr" {
_, ipn, err := net.ParseCIDR(parts[2] + "/" + parts[4])
if err != nil {
return nil, err
}
return ipn, nil
}
return nil, errors.New("invalid format")
}
package mask
import (
"net"
"testing"
)
func TestFiltered(t *testing.T) {
var tests = map[string]map[string]bool{
"/ip4/10.0.0.0/ipcidr/8": map[string]bool{
"10.3.3.4": true,
"10.3.4.4": true,
"10.4.4.4": true,
"15.52.34.3": false,
},
"/ip4/192.168.0.0/ipcidr/16": map[string]bool{
"192.168.0.0": true,
"192.168.1.0": true,
"192.1.0.0": false,
"10.4.4.4": false,
},
}
for mask, set := range tests {
m, err := NewMask(mask)
if err != nil {
t.Fatal(err)
}
for addr, val := range set {
ip := net.ParseIP(addr)
if m.Contains(ip) != val {
t.Fatalf("expected contains(%s, %s) == %s", mask, addr, val)
}
}
}
}
...@@ -13,17 +13,17 @@ import ( ...@@ -13,17 +13,17 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"net"
"time" "time"
b58 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58" b58 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58"
ctxgroup "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup" ctxgroup "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup"
ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore"
ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
mamask "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/whyrusleeping/multiaddr-filter"
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
metrics "github.com/ipfs/go-ipfs/metrics"
eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog"
diag "github.com/ipfs/go-ipfs/diagnostics" diag "github.com/ipfs/go-ipfs/diagnostics"
metrics "github.com/ipfs/go-ipfs/metrics"
ic "github.com/ipfs/go-ipfs/p2p/crypto" ic "github.com/ipfs/go-ipfs/p2p/crypto"
discovery "github.com/ipfs/go-ipfs/p2p/discovery" discovery "github.com/ipfs/go-ipfs/p2p/discovery"
p2phost "github.com/ipfs/go-ipfs/p2p/host" p2phost "github.com/ipfs/go-ipfs/p2p/host"
...@@ -32,6 +32,7 @@ import ( ...@@ -32,6 +32,7 @@ import (
swarm "github.com/ipfs/go-ipfs/p2p/net/swarm" swarm "github.com/ipfs/go-ipfs/p2p/net/swarm"
addrutil "github.com/ipfs/go-ipfs/p2p/net/swarm/addr" addrutil "github.com/ipfs/go-ipfs/p2p/net/swarm/addr"
peer "github.com/ipfs/go-ipfs/p2p/peer" peer "github.com/ipfs/go-ipfs/p2p/peer"
eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog"
routing "github.com/ipfs/go-ipfs/routing" routing "github.com/ipfs/go-ipfs/routing"
dht "github.com/ipfs/go-ipfs/routing/dht" dht "github.com/ipfs/go-ipfs/routing/dht"
...@@ -254,7 +255,18 @@ func (n *IpfsNode) startOnlineServices(ctx context.Context, routingOption Routin ...@@ -254,7 +255,18 @@ func (n *IpfsNode) startOnlineServices(ctx context.Context, routingOption Routin
// Set reporter // Set reporter
n.Reporter = metrics.NewBandwidthCounter() n.Reporter = metrics.NewBandwidthCounter()
peerhost, err := hostOption(ctx, n.Identity, n.Peerstore, n.Reporter) // get undialable addrs from config
cfg := n.Repo.Config()
var addrfilter []*net.IPNet
for _, s := range cfg.DialBlocklist {
f, err := mamask.NewMask(s)
if err != nil {
return fmt.Errorf("incorrectly formatter address filter in config: %s", s)
}
addrfilter = append(addrfilter, f)
}
peerhost, err := hostOption(ctx, n.Identity, n.Peerstore, n.Reporter, addrfilter)
if err != nil { if err != nil {
return err return err
} }
...@@ -508,12 +520,12 @@ func listenAddresses(cfg *config.Config) ([]ma.Multiaddr, error) { ...@@ -508,12 +520,12 @@ func listenAddresses(cfg *config.Config) ([]ma.Multiaddr, error) {
return listen, nil return listen, nil
} }
type HostOption func(ctx context.Context, id peer.ID, ps peer.Peerstore, bwr metrics.Reporter) (p2phost.Host, error) type HostOption func(ctx context.Context, id peer.ID, ps peer.Peerstore, bwr metrics.Reporter, fs []*net.IPNet) (p2phost.Host, error)
var DefaultHostOption HostOption = constructPeerHost var DefaultHostOption HostOption = constructPeerHost
// isolates the complex initialization steps // isolates the complex initialization steps
func constructPeerHost(ctx context.Context, id peer.ID, ps peer.Peerstore, bwr metrics.Reporter) (p2phost.Host, error) { func constructPeerHost(ctx context.Context, id peer.ID, ps peer.Peerstore, bwr metrics.Reporter, fs []*net.IPNet) (p2phost.Host, error) {
// no addresses to begin with. we'll start later. // no addresses to begin with. we'll start later.
network, err := swarm.NewNetwork(ctx, nil, id, ps, bwr) network, err := swarm.NewNetwork(ctx, nil, id, ps, bwr)
......
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
key "github.com/ipfs/go-ipfs/blocks/key" key "github.com/ipfs/go-ipfs/blocks/key"
ic "github.com/ipfs/go-ipfs/p2p/crypto" ic "github.com/ipfs/go-ipfs/p2p/crypto"
filter "github.com/ipfs/go-ipfs/p2p/net/filter"
peer "github.com/ipfs/go-ipfs/p2p/peer" peer "github.com/ipfs/go-ipfs/p2p/peer"
msgio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-msgio" msgio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-msgio"
...@@ -86,6 +87,8 @@ type Listener interface { ...@@ -86,6 +87,8 @@ type Listener interface {
// LocalPeer is the identity of the local Peer. // LocalPeer is the identity of the local Peer.
LocalPeer() peer.ID LocalPeer() peer.ID
SetAddrFilters(*filter.Filters)
// Close closes the listener. // Close closes the listener.
// Any blocked Accept operations will be unblocked and return errors. // Any blocked Accept operations will be unblocked and return errors.
Close() error Close() error
......
...@@ -13,6 +13,7 @@ import ( ...@@ -13,6 +13,7 @@ import (
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
ic "github.com/ipfs/go-ipfs/p2p/crypto" ic "github.com/ipfs/go-ipfs/p2p/crypto"
filter "github.com/ipfs/go-ipfs/p2p/net/filter"
peer "github.com/ipfs/go-ipfs/p2p/peer" peer "github.com/ipfs/go-ipfs/p2p/peer"
) )
...@@ -26,6 +27,8 @@ type listener struct { ...@@ -26,6 +27,8 @@ type listener struct {
local peer.ID // LocalPeer is the identity of the local Peer local peer.ID // LocalPeer is the identity of the local Peer
privk ic.PrivKey // private key to use to initialize secure conns privk ic.PrivKey // private key to use to initialize secure conns
filters *filter.Filters
wrapper ConnWrapper wrapper ConnWrapper
cg ctxgroup.ContextGroup cg ctxgroup.ContextGroup
...@@ -45,6 +48,10 @@ func (l *listener) String() string { ...@@ -45,6 +48,10 @@ func (l *listener) String() string {
return fmt.Sprintf("<Listener %s %s>", l.local, l.Multiaddr()) return fmt.Sprintf("<Listener %s %s>", l.local, l.Multiaddr())
} }
func (l *listener) SetAddrFilters(fs *filter.Filters) {
l.filters = fs
}
// Accept waits for and returns the next connection to the listener. // Accept waits for and returns the next connection to the listener.
// Note that unfortunately this // Note that unfortunately this
func (l *listener) Accept() (net.Conn, error) { func (l *listener) Accept() (net.Conn, error) {
...@@ -81,6 +88,12 @@ func (l *listener) Accept() (net.Conn, error) { ...@@ -81,6 +88,12 @@ func (l *listener) Accept() (net.Conn, error) {
} }
log.Debugf("listener %s got connection: %s <---> %s", l, maconn.LocalMultiaddr(), maconn.RemoteMultiaddr()) log.Debugf("listener %s got connection: %s <---> %s", l, maconn.LocalMultiaddr(), maconn.RemoteMultiaddr())
if l.filters != nil && l.filters.AddrBlocked(maconn.RemoteMultiaddr()) {
log.Debugf("blocked connection from %s", maconn.RemoteMultiaddr())
maconn.Close()
continue
}
// If we have a wrapper func, wrap this conn // If we have a wrapper func, wrap this conn
if l.wrapper != nil { if l.wrapper != nil {
maconn = l.wrapper(maconn) maconn = l.wrapper(maconn)
......
package filter
import (
"net"
"strings"
ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
manet "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
)
type Filters struct {
filters []*net.IPNet
}
func (fs *Filters) AddDialFilter(f *net.IPNet) {
fs.filters = append(fs.filters, f)
}
func (f *Filters) AddrBlocked(a ma.Multiaddr) bool {
_, addr, err := manet.DialArgs(a)
if err != nil {
// if we cant parse it, its probably not blocked
return false
}
ipstr := strings.Split(addr, ":")[0]
ip := net.ParseIP(ipstr)
for _, ft := range f.filters {
if ft.Contains(ip) {
return true
}
}
return false
}
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
metrics "github.com/ipfs/go-ipfs/metrics" metrics "github.com/ipfs/go-ipfs/metrics"
inet "github.com/ipfs/go-ipfs/p2p/net" inet "github.com/ipfs/go-ipfs/p2p/net"
filter "github.com/ipfs/go-ipfs/p2p/net/filter"
addrutil "github.com/ipfs/go-ipfs/p2p/net/swarm/addr" addrutil "github.com/ipfs/go-ipfs/p2p/net/swarm/addr"
peer "github.com/ipfs/go-ipfs/p2p/peer" peer "github.com/ipfs/go-ipfs/p2p/peer"
eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog"
...@@ -50,6 +51,9 @@ type Swarm struct { ...@@ -50,6 +51,9 @@ type Swarm struct {
notifmu sync.RWMutex notifmu sync.RWMutex
notifs map[inet.Notifiee]ps.Notifiee notifs map[inet.Notifiee]ps.Notifiee
// filters for addresses that shouldnt be dialed
Filters *filter.Filters
cg ctxgroup.ContextGroup cg ctxgroup.ContextGroup
bwc metrics.Reporter bwc metrics.Reporter
} }
...@@ -64,13 +68,14 @@ func NewSwarm(ctx context.Context, listenAddrs []ma.Multiaddr, ...@@ -64,13 +68,14 @@ func NewSwarm(ctx context.Context, listenAddrs []ma.Multiaddr,
} }
s := &Swarm{ s := &Swarm{
swarm: ps.NewSwarm(PSTransport), swarm: ps.NewSwarm(PSTransport),
local: local, local: local,
peers: peers, peers: peers,
cg: ctxgroup.WithContext(ctx), cg: ctxgroup.WithContext(ctx),
dialT: DialTimeout, dialT: DialTimeout,
notifs: make(map[inet.Notifiee]ps.Notifiee), notifs: make(map[inet.Notifiee]ps.Notifiee),
bwc: bwc, bwc: bwc,
Filters: new(filter.Filters),
} }
// configure Swarm // configure Swarm
......
...@@ -303,6 +303,7 @@ func (s *Swarm) dial(ctx context.Context, p peer.ID) (*Conn, error) { ...@@ -303,6 +303,7 @@ func (s *Swarm) dial(ctx context.Context, p peer.ID) (*Conn, error) {
ila, _ := s.InterfaceListenAddresses() ila, _ := s.InterfaceListenAddresses()
remoteAddrs = addrutil.Subtract(remoteAddrs, ila) remoteAddrs = addrutil.Subtract(remoteAddrs, ila)
remoteAddrs = addrutil.Subtract(remoteAddrs, s.peers.Addrs(s.local)) remoteAddrs = addrutil.Subtract(remoteAddrs, s.peers.Addrs(s.local))
log.Debugf("%s swarm dialing %s -- local:%s remote:%s", s.local, p, s.ListenAddresses(), remoteAddrs) log.Debugf("%s swarm dialing %s -- local:%s remote:%s", s.local, p, s.ListenAddresses(), remoteAddrs)
if len(remoteAddrs) == 0 { if len(remoteAddrs) == 0 {
err := errors.New("peer has no addresses") err := errors.New("peer has no addresses")
...@@ -310,6 +311,13 @@ func (s *Swarm) dial(ctx context.Context, p peer.ID) (*Conn, error) { ...@@ -310,6 +311,13 @@ func (s *Swarm) dial(ctx context.Context, p peer.ID) (*Conn, error) {
return nil, err return nil, err
} }
remoteAddrs = s.filterAddrs(remoteAddrs)
if len(remoteAddrs) == 0 {
err := errors.New("all adresses for peer have been filtered out")
logdial["error"] = err
return nil, err
}
// open connection to peer // open connection to peer
d := &conn.Dialer{ d := &conn.Dialer{
Dialer: manet.Dialer{ Dialer: manet.Dialer{
...@@ -454,6 +462,16 @@ func (s *Swarm) dialAddr(ctx context.Context, d *conn.Dialer, p peer.ID, addr ma ...@@ -454,6 +462,16 @@ func (s *Swarm) dialAddr(ctx context.Context, d *conn.Dialer, p peer.ID, addr ma
return connC, nil return connC, nil
} }
func (s *Swarm) filterAddrs(addrs []ma.Multiaddr) []ma.Multiaddr {
var out []ma.Multiaddr
for _, a := range addrs {
if !s.Filters.AddrBlocked(a) {
out = append(out, a)
}
}
return out
}
// dialConnSetup is the setup logic for a connection from the dial side. it // dialConnSetup is the setup logic for a connection from the dial side. it
// needs to add the Conn to the StreamSwarm, then run newConnSetup // needs to add the Conn to the StreamSwarm, then run newConnSetup
func dialConnSetup(ctx context.Context, s *Swarm, connC conn.Conn) (*Conn, error) { func dialConnSetup(ctx context.Context, s *Swarm, connC conn.Conn) (*Conn, error) {
......
...@@ -69,6 +69,8 @@ func (s *Swarm) setupListener(maddr ma.Multiaddr) error { ...@@ -69,6 +69,8 @@ func (s *Swarm) setupListener(maddr ma.Multiaddr) error {
return err return err
} }
list.SetAddrFilters(s.Filters)
if cw, ok := list.(conn.ListenerConnWrapper); ok { if cw, ok := list.(conn.ListenerConnWrapper); ok {
cw.SetConnWrapper(func(c manet.Conn) manet.Conn { cw.SetConnWrapper(func(c manet.Conn) manet.Conn {
return mconn.WrapConn(s.bwc, c) return mconn.WrapConn(s.bwc, c)
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"io" "io"
"net"
"sync" "sync"
"testing" "testing"
"time" "time"
...@@ -270,3 +271,60 @@ func TestConnHandler(t *testing.T) { ...@@ -270,3 +271,60 @@ func TestConnHandler(t *testing.T) {
default: default:
} }
} }
func TestAddrBlocking(t *testing.T) {
ctx := context.Background()
swarms := makeSwarms(ctx, t, 2)
swarms[0].SetConnHandler(func(conn *Conn) {
t.Fatal("no connections should happen!")
})
_, block, err := net.ParseCIDR("127.0.0.1/8")
if err != nil {
t.Fatal(err)
}
swarms[1].Filters.AddDialFilter(block)
swarms[1].peers.AddAddr(swarms[0].LocalPeer(), swarms[0].ListenAddresses()[0], peer.PermanentAddrTTL)
_, err = swarms[1].Dial(context.TODO(), swarms[0].LocalPeer())
if err == nil {
t.Fatal("dial should have failed")
}
swarms[0].peers.AddAddr(swarms[1].LocalPeer(), swarms[1].ListenAddresses()[0], peer.PermanentAddrTTL)
_, err = swarms[0].Dial(context.TODO(), swarms[1].LocalPeer())
if err == nil {
t.Fatal("dial should have failed")
}
}
func TestFilterBounds(t *testing.T) {
ctx := context.Background()
swarms := makeSwarms(ctx, t, 2)
conns := make(chan struct{}, 8)
swarms[0].SetConnHandler(func(conn *Conn) {
conns <- struct{}{}
})
// Address that we wont be dialing from
_, block, err := net.ParseCIDR("192.0.0.1/8")
if err != nil {
t.Fatal(err)
}
// set filter on both sides, shouldnt matter
swarms[1].Filters.AddDialFilter(block)
swarms[0].Filters.AddDialFilter(block)
connectSwarms(t, ctx, swarms)
select {
case <-time.After(time.Second):
t.Fatal("should have gotten connection")
case <-conns:
fmt.Println("got connect")
}
}
...@@ -26,6 +26,7 @@ type Config struct { ...@@ -26,6 +26,7 @@ type Config struct {
Tour Tour // local node's tour position Tour Tour // local node's tour position
Gateway Gateway // local node's gateway server options Gateway Gateway // local node's gateway server options
SupernodeRouting SupernodeClientConfig // local node's routing servers (if SupernodeRouting enabled) SupernodeRouting SupernodeClientConfig // local node's routing servers (if SupernodeRouting enabled)
DialBlocklist []string
Log Log Log Log
} }
......
...@@ -6,4 +6,5 @@ import _ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/dustin/go-hum ...@@ -6,4 +6,5 @@ import _ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/dustin/go-hum
// similar to the above, only used in the tests makefile // similar to the above, only used in the tests makefile
import _ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/whyrusleeping/iptb" import _ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/whyrusleeping/iptb"
import _ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/chriscool/go-sleep" import _ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/chriscool/go-sleep"
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment