Add DROP_CLIENT env variable (#432)
This will disconnect any client based on their version string, using a regular expression. Useful for dropping buggy/unsupported clients.
This commit is contained in:
parent
02ab8e5742
commit
90f28314d2
@ -201,6 +201,12 @@ These environment variables are optional:
|
||||
|
||||
If you are not sure what this means leave it unset.
|
||||
|
||||
.. envvar:: DROP_CLIENT
|
||||
|
||||
Set a regular expression to disconnect any client based on their
|
||||
version string. For example to drop versions from 1.0 to 1.2 use
|
||||
the regex ``1\.[0-2]\.\d+``.
|
||||
|
||||
|
||||
Resource Usage Limits
|
||||
=====================
|
||||
|
||||
@ -53,6 +53,17 @@ class EnvBase(lib_util.LoggedClass):
|
||||
raise cls.Error('cannot convert envvar {} value {} to an integer'
|
||||
.format(envvar, value))
|
||||
|
||||
@classmethod
|
||||
def custom(cls, envvar, default, parse):
|
||||
value = environ.get(envvar)
|
||||
if value is None:
|
||||
return default
|
||||
try:
|
||||
return parse(value)
|
||||
except Exception as e:
|
||||
raise cls.Error('cannot parse envvar {} value {}'
|
||||
.format(envvar, value)) from e
|
||||
|
||||
@classmethod
|
||||
def obsolete(cls, envvars):
|
||||
bad = [envvar for envvar in envvars if environ.get(envvar)]
|
||||
|
||||
@ -254,6 +254,9 @@ class Controller(ServerBase):
|
||||
self.logger.info('max subscriptions per session: {:,d}'
|
||||
.format(self.env.max_session_subs))
|
||||
self.logger.info('bands: {}'.format(self.bands))
|
||||
if self.env.drop_client is not None:
|
||||
self.logger.info('drop clients matching: {}'
|
||||
.format(self.env.drop_client.pattern))
|
||||
await self.start_external_servers()
|
||||
|
||||
async def start_external_servers(self):
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
'''Class for handling environment configuration and defaults.'''
|
||||
|
||||
|
||||
import re
|
||||
import resource
|
||||
from collections import namedtuple
|
||||
from ipaddress import ip_address
|
||||
@ -67,6 +68,7 @@ class Env(EnvBase):
|
||||
self.max_session_subs = self.integer('MAX_SESSION_SUBS', 50000)
|
||||
self.bandwidth_limit = self.integer('BANDWIDTH_LIMIT', 2000000)
|
||||
self.session_timeout = self.integer('SESSION_TIMEOUT', 600)
|
||||
self.drop_client = self.custom("DROP_CLIENT", None, re.compile)
|
||||
|
||||
# Identities
|
||||
clearnet_identity = self.clearnet_identity()
|
||||
|
||||
@ -358,6 +358,10 @@ class ElectrumX(SessionBase):
|
||||
protocol_version: the protocol version spoken by the client
|
||||
'''
|
||||
if client_name:
|
||||
if self.env.drop_client is not None and \
|
||||
self.env.drop_client.match(client_name):
|
||||
raise RPCError('unsupported client: {}'
|
||||
.format(client_name), JSONRPC.FATAL_ERROR)
|
||||
self.client = str(client_name)[:17]
|
||||
try:
|
||||
self.client_version = tuple(int(part) for part
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
|
||||
import pytest
|
||||
|
||||
@ -340,3 +341,13 @@ def test_tor_identity():
|
||||
assert ident.host == tor_host
|
||||
assert ident.tcp_port == 234
|
||||
assert ident.ssl_port == 432
|
||||
|
||||
def test_ban_versions():
|
||||
e = Env()
|
||||
assert e.drop_client is None
|
||||
ban_re = '1\.[0-2]\.\d+?[_\w]*'
|
||||
os.environ['DROP_CLIENT'] = ban_re
|
||||
e = Env()
|
||||
assert e.drop_client == re.compile(ban_re)
|
||||
assert e.drop_client.match("1.2.3_buggy_client")
|
||||
assert e.drop_client.match("1.3.0_good_client") is None
|
||||
|
||||
Loading…
Reference in New Issue
Block a user