From db417a0275ccb9dc8e032473325c50b34bc934eb Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Tue, 9 Jan 2018 19:12:46 -0800 Subject: [PATCH 1/3] server: adds hybrid black/whitelist filtering to user agents --- server.go | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/server.go b/server.go index bc4affde3c..ac6ab2f7ca 100644 --- a/server.go +++ b/server.go @@ -246,6 +246,14 @@ type server struct { // messages for each filter type. cfCheckptCaches map[wire.FilterType][]cfHeaderKV cfCheckptCachesMtx sync.RWMutex + + // agentBlacklist is a list of blacklisted substrings by which to filter + // user agents. + agentBlacklist []string + + // agentWhitelist is a list of whitelisted user agent substrings, no + // whitelisting will be applied if the list is empty or nil. + agentWhitelist []string } // serverPeer extends the peer to maintain state shared by the server and @@ -1470,6 +1478,12 @@ func (s *server) handleAddPeerMsg(state *peerState, sp *serverPeer) bool { return false } + // Disconnect peers with unwanted user agents. + if sp.HasUndesiredUserAgent(s.agentBlacklist, s.agentWhitelist) { + sp.Disconnect() + return false + } + // Ignore new peers if we're shutting down. if atomic.LoadInt32(&s.shutdown) != 0 { srvrLog.Infof("New peer %s ignored - server is shutting down", sp) @@ -2421,7 +2435,10 @@ func setupRPCListeners() ([]net.Listener, error) { // newServer returns a new btcd server configured to listen on addr for the // bitcoin network type specified by chainParams. Use start to begin accepting // connections from peers. -func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Params, interrupt <-chan struct{}) (*server, error) { +func newServer(listenAddrs, agentBlacklist, agentWhitelist []string, + db database.DB, chainParams *chaincfg.Params, + interrupt <-chan struct{}) (*server, error) { + services := defaultServices if cfg.NoPeerBloomFilters { services &^= wire.SFNodeBloom @@ -2445,6 +2462,9 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param } } + srvrLog.Infof("Using agent blacklist: %s and whitelist: %s", + agentBlacklist, agentWhitelist) + s := server{ chainParams: chainParams, addrManager: amgr, @@ -2464,6 +2484,8 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param sigCache: txscript.NewSigCache(cfg.SigCacheMaxSize), hashCache: txscript.NewHashCache(cfg.SigCacheMaxSize), cfCheckptCaches: make(map[wire.FilterType][]cfHeaderKV), + agentBlacklist: agentBlacklist, + agentWhitelist: agentWhitelist, } // Create the transaction and address indexes if needed. @@ -3016,3 +3038,47 @@ func mergeCheckpoints(defaultCheckpoints, additional []chaincfg.Checkpoint) []ch sort.Sort(checkpointSorter(checkpoints)) return checkpoints } + +// HasUndesiredUserAgent determines whether the server should continue to pursue +// a connection with this peer based on its advertised user agent. It performs +// the following steps: +// 1) Reject the peer if it contains a blacklisted agent. +// 2) If no whitelist is provided, accept all user agents. +// 3) Accept the peer if it contains a whitelisted agent. +// 4) Reject all other peers. +func (sp *serverPeer) HasUndesiredUserAgent(blacklistedAgents, + whitelistedAgents []string) bool { + + agent := sp.UserAgent() + + // First, if peer's user agent contains any blacklisted substring, we + // will ignore the connection request. + for _, blacklistedAgent := range blacklistedAgents { + if strings.Contains(agent, blacklistedAgent) { + srvrLog.Debugf("Ignoring peer %s, user agent "+ + "contains blacklisted user agent: %s", sp, + agent) + return true + } + } + + // If no whitelist is provided, we will accept all user agents. + if len(whitelistedAgents) == 0 { + return false + } + + // Peer's user agent passed blacklist. Now check to see if it contains + // one of our whitelisted user agents, if so accept. + for _, whitelistedAgent := range whitelistedAgents { + if strings.Contains(agent, whitelistedAgent) { + return false + } + } + + // Otherwise, the peer's user agent was not included in our whitelist. + // Ignore just in case it could stall the initial block download. + srvrLog.Debugf("Ignoring peer %s, user agent: %s not found in "+ + "whitelist", sp, agent) + + return true +} From 0112a9e6572f71d8eb3f8eedf3db5606d9d9acff Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Tue, 9 Jan 2018 19:13:25 -0800 Subject: [PATCH 2/3] btcd: pass user agent black and white lists to server --- btcd.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/btcd.go b/btcd.go index d463f74dee..c6df94c5e3 100644 --- a/btcd.go +++ b/btcd.go @@ -14,6 +14,7 @@ import ( "runtime" "runtime/debug" "runtime/pprof" + "strings" "github.com/roasbeef/btcd/blockchain/indexers" "github.com/roasbeef/btcd/database" @@ -144,9 +145,13 @@ func btcdMain(serverChan chan<- *server) error { return nil } + // Parse the black and whitelists as comma separated lists. + agentBlacklist := strings.Split(cfg.AgentBlacklist, ",") + agentWhitelist := strings.Split(cfg.AgentWhitelist, ",") + // Create server and start it. - server, err := newServer(cfg.Listeners, db, activeNetParams.Params, - interrupt) + server, err := newServer(cfg.Listeners, agentBlacklist, agentWhitelist, + db, activeNetParams.Params, interrupt) if err != nil { // TODO: this logging could do with some beautifying. btcdLog.Errorf("Unable to start server on %v: %v", From 036b347e5b5bde1ed75b47f2f4b5fe999000c3c6 Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Tue, 9 Jan 2018 19:13:50 -0800 Subject: [PATCH 3/3] config: adds AgentWhitelist and AgentBlacklist to config --- config.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/config.go b/config.go index 4a38ca768b..1288719a3d 100644 --- a/config.go +++ b/config.go @@ -20,6 +20,8 @@ import ( "strings" "time" + "github.com/btcsuite/go-socks/socks" + flags "github.com/jessevdk/go-flags" "github.com/roasbeef/btcd/blockchain" "github.com/roasbeef/btcd/chaincfg" "github.com/roasbeef/btcd/chaincfg/chainhash" @@ -28,8 +30,6 @@ import ( _ "github.com/roasbeef/btcd/database/ffldb" "github.com/roasbeef/btcd/mempool" "github.com/roasbeef/btcutil" - "github.com/btcsuite/go-socks/socks" - flags "github.com/jessevdk/go-flags" ) const ( @@ -104,6 +104,8 @@ type config struct { BanDuration time.Duration `long:"banduration" description:"How long to ban misbehaving peers. Valid time units are {s, m, h}. Minimum 1 second"` BanThreshold uint32 `long:"banthreshold" description:"Maximum allowed ban score before disconnecting and banning misbehaving peers."` Whitelists []string `long:"whitelist" description:"Add an IP network or IP that will not be banned. (eg. 192.168.1.0/24 or ::1)"` + AgentBlacklist string `long:"agentblacklist" description:"A comma separated list of user agents substrings to which btcd will not allow connections."` + AgentWhitelist string `long:"agentwhitelist" description:"A comma separated list of user agent substrings to which must be present before allowing a particular connection, the whitelist is applied after the blacklist. An empty whitelist will allow all agents that do not fail the blacklist."` RPCUser string `short:"u" long:"rpcuser" description:"Username for RPC connections"` RPCPass string `short:"P" long:"rpcpass" default-mask:"-" description:"Password for RPC connections"` RPCLimitUser string `long:"rpclimituser" description:"Username for limited RPC connections"`