Banning: Manually configured by the user via the setban RPC method. No
outbound/inbound connections will be established with a banned address or
subnet and the address will not be gossiped. Banned addresses are persisted in
banlist.dat on shutdown and reloaded on startup.
Discouragement: Peers are assigned a misbehavior
score
for sending us messages that violate the protocol (e.g. a too large
payload, or headers that don’t connect to our chain). If a peer accumulates a
misbehavior score of 100, we disconnect and discourage that address. Inbound
connections from discouraged addresses are allowed but preferred for eviction.
Outbound connections are not made to discouraged addresses, and those addresses
are not gossiped. Discouraged addresses are not persisted to disk on shutdown.
Neither banning nor discouragement fully protect against a determined
attacker, since IP addresses are inexpensive.
If implemented without care, automatic banning would increase the risk of a
network split.
The BanMan interface allows banning and unbanning via CNetAddr and
CSubNet. This PR consolidates to only accept CSubNet (an IP address is a
subnet of 1).
This PR also adds two new RPC commands:
setban listbanned ip: lists all the ban entries that include ip
setban removeall ip: removes all ban entries that include ip
Questions
What are some use cases for banning specific IP addresses or subnets?
Is automatic banning/disconnection an effective DoS countermeasure for
misbehaving peers?
How can automatic banning/disconnection be abused by an attacker?
Would well-formed setban ip add/setban ip remove RPC calls ever throw an
error? Should they?
On master, what happens if you try to ban the same IP address twice? How does
this affect the UX for ban scripts?
Before this PR, if setban 192.168.1.1/24 add is followed by setban
192.168.1.1 add, an error is thrown. However you can setban 192.168.1.1/32
add. Why does this happen?
Are there any downsides to allowing overlapping ban entries as this PR does?
How can the user figure out which ban entries might be blocking outbound
connections to a certain IP (before and after this PR)?
How can the user completely unban an IP (before and after this PR)?
<dhruvm> Those are all great answers! As an example, we may want to ban addresses or subnets that are used by crawlers like bitnodes.io, etc. There are people in the bitcoin community that maintain banlists shared widely.
<theStack> i think that question is also answered in the notes: "Neither banning nor discouragement fully protect against a determined attacker, since IP addresses are inexpensive."
<dhruvm> Automatic banning/disconnection is not an effective DoS countermeasure as the primary key for banlists are IP addresses that are inexpensive. An attacker can change their banned identity for ~$0.01 per hour.
<felixweis> maybe give the rpc a single ip inside an AS we want to discourage connections to/from and then the system does a lookup into the ASMAP and deduces the rest
<dhruvm> some of the conversation is leading nicely into the next question: How can automatic banning/disconnection be abused by an attacker, or create a network split?
<enjoying> dhruvm I think verifying the node exists is less of a need, surveillance's goal is just to winnow the full set down to a more manageable set to go after
<felixweis> dont think its possible right now, but if there would be logic that once you have many attacks coming from the same subnet but constantly changing ip suffixes it might feel like a mitigation
<dhruvm> thomasb06 setpill _0x0ff: automatically banning a subnet for a misbehaving ip wouldn't really make sense. How would you select how large to make the subnet?
<glozow> wait are we talking about a hardcoded automatic subnet ban, or are we talking about a programmatic rule for banning based on some misbehavior?
<dhruvm> Automatic banning/disconnection can increase network partition risk if not implemented well. Segwit activation can illustrate this risk. Upgraded segwit nodes had stricter validation rules. If they punished older nodes that published invalid blocks(containing invalid segwit transactions), we would have partition risk. Luckily nodes are not punished in such cases after a recent consensus
<setpill> norisgOG_: it would be an attack, a malicious entity could feed innocent pre-segwit nodes bad segwit txes that would lead to them being cut off by segwit nodes
<enjoying> emzy I think I'm not understanding something, are you saying that peers to connect through an onion address aren't specified in a manner we can selectively tell the node, "disconnect peer at jx2o6mp3dtql4bn2.onion" for example?
<jnewbery> The risk was more in a block containing an invalid segwit transaction. Old nodes would reject unconfirmed segwit transactions because of policy, but they'd accept blocks containing segwit transactions (which is what makes segwit a soft fork)
<michaelfolkson> There is a difference between chain split (half the network follows one chain, half the network another) due to one disputed SegWit transaction and peers banning other peers for sending a particular transaction
<_0x0ff> If user is adding/removing things in as it's expected it shouldn't. Errors should only be thrown when user is trying to add/remove something ambiguous
<AnthonyRonning> In master, it does throw an error if the IP is already banned. I don't think it should because the intent is still correct even if already banned.
<dhruvm> On master, `setban ip add` throws an error if a subnet is already banned (as an ip or is a member of a banned subnet), and `setban ip remove` throws an error if we are trying to unban an ip/subnet that is not banned.
<_0x0ff> An error is thrown, which means ban scripts need to take that error into account and then remove the ban and re-add it. This complicates ban scripts because a narrower ban would be rejected in case there's a wider ban taken into effect and handling this is a mess.
<AnthonyRonning> Error is thrown in master, it makes it complicated for scripts to complete without error if they're pulling from a list and don't know exact state of the banlist.
<dhruvm> Q: Before this PR, if setban 192.168.1.1/24 add is followed by setban 192.168.1.1 add, an error is thrown. However you can setban 192.168.1.1/32 add. Why does this happen?
<dhruvm> On master, `BanMan::IsBanned(CNetAddr&)` checks if the provided ip address is a member of any banned subnet. However, `BanMan::IsBanned(CSubNet&)` only checks for exact matches: https://github.com/bitcoin/bitcoin/blob/master/src/banman.cpp#L77-L104. Since the rpc code for setban checks `IsBanned` prior to banning, the behavior is different for ip addresses and subnets.
<AnthonyRonning> Is there any particular reason for that? Couldn't it deduce that there's no reason to add a smaller subnet if the larger is already banned?
<setpill> AnthonyRonning: actually sishir just linked a comment by gmax that smaller subnet bans still make sense if they are longer duration than the bigger subnet ban
<dhruvm> AnthonyRonning: setpill: longer, narrower bans and shorter, wider bans make sense and there is potential to consolidate ban entries. However #19825 no longer consolidates.
<dhruvm> #19825 just adds new ban entries instead of trying to consolidate/reject overlapping ones. There is some cost to more entries in the banmap(memory) and increased cost of `IsBanned`(cpu/time). However, the most prominent place `IsBanned` is invoked is in `CConnman::AcceptConnection` which is relatively infrequent. The reduced code complexity is well-worth it.
<theStack> don't know what a typical banlist looks like but i could also imagine that overlapping ban ranges are actually quite rare, so it's even less of a problem?
<_0x0ff> Before: user had to call `listbanned` which returned all banned ranged and look in there ... After: `listbanned <ip>` outputs all banned ranges that match a given ip
<_0x0ff> Before: i'm not 100% certain, but I believe user had to call `listbanned` and go over the list and manually call `setban <subnet> remove` in order to unban a particular IP ... After: `setban <ip> removeall` removes all subnets that would ban a given ip or subnet
<AnthonyRonning> Before, `listbanned` and find all subnets to remove, or call `setban ip remove` on all subnets, ignoring errors if a subnet was not already banned. After, `setban removeall ip`