From d3ecdc91a9ac0305ca9c8af02b0dacb285e2a5cb 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 | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/server.go b/server.go index 42f34da5..ab2c31fe 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 @@ -1586,6 +1594,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) @@ -3133,3 +3147,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 0e073b8058b634abfc618f983d3f978813050ada Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Tue, 9 Jan 2018 19:13:50 -0800 Subject: [PATCH 2/3] config: adds AgentWhitelist and AgentBlacklist to config --- config.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config.go b/config.go index e004e428..5934f6bc 100644 --- a/config.go +++ b/config.go @@ -106,6 +106,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-agent substrings which will cause btcd to reject any peers whose user-agent contains any of the blacklisted substrings."` + AgentWhitelist []string `long:"agentwhitelist" description:"A comma separated list of user-agent substrings which will cause btcd to require all peers' user-agents to contain one of the whitelisted substrings. The blacklist is applied before the blacklist, and 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"` From 39592eba78bc139b9eeecdff978905d655c674cc Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Tue, 9 Jan 2018 19:13:25 -0800 Subject: [PATCH 3/3] btcd: pass user agent black and white lists to server --- btcd.go | 4 ++-- server.go | 14 +++++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/btcd.go b/btcd.go index a0ccb3ea..c17d6ab4 100644 --- a/btcd.go +++ b/btcd.go @@ -145,8 +145,8 @@ func btcdMain(serverChan chan<- *server) error { } // Create server and start it. - server, err := newServer(cfg.Listeners, db, activeNetParams.Params, - interrupt) + server, err := newServer(cfg.Listeners, cfg.AgentBlacklist, + cfg.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", diff --git a/server.go b/server.go index ab2c31fe..d8fac256 100644 --- a/server.go +++ b/server.go @@ -2552,7 +2552,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 @@ -2576,6 +2579,13 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param } } + if len(agentBlacklist) > 0 { + srvrLog.Infof("User-agent blacklist %s", agentBlacklist) + } + if len(agentWhitelist) > 0 { + srvrLog.Infof("User-agent whitelist %s", agentWhitelist) + } + s := server{ chainParams: chainParams, addrManager: amgr, @@ -2595,6 +2605,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.