Merge branch 'block_relays' into 'master'

Block relays

See merge request pleroma/relay!26
This commit is contained in:
Haelwenn 2020-12-07 15:38:17 +00:00
commit 83d56cb570
5 changed files with 56 additions and 10 deletions

View File

@ -20,4 +20,11 @@ ap:
whitelist_enabled: false whitelist_enabled: false
whitelist: whitelist:
- 'good-instance.example.com' - 'good-instance.example.com'
- 'another.good-instance.example.com' - 'another.good-instance.example.com'
# uncomment the lines below to prevent certain activitypub software from posting
# to the relay (all known relays by default). this uses the software name in nodeinfo
#blocked_software:
#- 'activityrelay'
#- 'aoderelay'
#- 'social.seattle.wa.us-relay'
#- 'unciarelay'

View File

@ -9,9 +9,13 @@ import yaml
def load_config(): def load_config():
with open('relay.yaml') as f: with open('relay.yaml') as f:
yaml_file = yaml.load(f) options = {}
whitelist = yaml_file['ap'].get('whitelist', [])
blocked = yaml_file['ap'].get('blocked_instances', []) ## Prevent a warning message for pyyaml 5.1+
if getattr(yaml, 'FullLoader', None):
options['Loader'] = yaml.FullLoader
yaml_file = yaml.load(f, **options)
config = { config = {
'db': yaml_file.get('db', 'relay.jsonld'), 'db': yaml_file.get('db', 'relay.jsonld'),
@ -19,9 +23,10 @@ def load_config():
'port': int(yaml_file.get('port', 8080)), 'port': int(yaml_file.get('port', 8080)),
'note': yaml_file.get('note', 'Make a note about your instance here.'), 'note': yaml_file.get('note', 'Make a note about your instance here.'),
'ap': { 'ap': {
'blocked_instances': [] if blocked is None else blocked, 'blocked_software': [v.lower() for v in yaml_file['ap'].get('blocked_software', [])],
'blocked_instances': yaml_file['ap'].get('blocked_instances', []),
'host': yaml_file['ap'].get('host', 'localhost'), 'host': yaml_file['ap'].get('host', 'localhost'),
'whitelist': [] if whitelist is None else whitelist, 'whitelist': yaml_file['ap'].get('whitelist', []),
'whitelist_enabled': yaml_file['ap'].get('whitelist_enabled', False) 'whitelist_enabled': yaml_file['ap'].get('whitelist_enabled', False)
} }
} }
@ -30,7 +35,6 @@ def load_config():
CONFIG = load_config() CONFIG = load_config()
from .http_signatures import http_signatures_middleware from .http_signatures import http_signatures_middleware

View File

@ -103,6 +103,29 @@ async def push_message_to_actor(actor, message, our_key_id):
logging.info('Caught %r while pushing to %r.', e, inbox) logging.info('Caught %r while pushing to %r.', e, inbox)
async def fetch_nodeinfo(domain):
headers = {'Accept': 'application/json'}
nodeinfo_url = None
wk_nodeinfo = await fetch_actor(f'https://{domain}/.well-known/nodeinfo', headers=headers)
if not wk_nodeinfo:
return
for link in wk_nodeinfo.get('links', ''):
if link['rel'] == 'http://nodeinfo.diaspora.software/ns/schema/2.0':
nodeinfo_url = link['href']
break
if not nodeinfo_url:
return
nodeinfo_data = await fetch_actor(nodeinfo_url, headers=headers)
software = nodeinfo_data.get('software')
return software.get('name') if software else None
async def follow_remote_actor(actor_uri): async def follow_remote_actor(actor_uri):
actor = await fetch_actor(actor_uri) actor = await fetch_actor(actor_uri)
@ -235,6 +258,7 @@ async def handle_follow(actor, data, request):
following = DATABASE.get('relay-list', []) following = DATABASE.get('relay-list', [])
inbox = get_actor_inbox(actor) inbox = get_actor_inbox(actor)
if urlsplit(inbox).hostname in AP_CONFIG['blocked_instances']: if urlsplit(inbox).hostname in AP_CONFIG['blocked_instances']:
return return
@ -294,6 +318,12 @@ async def inbox(request):
data = await request.json() data = await request.json()
instance = urlsplit(data['actor']).hostname instance = urlsplit(data['actor']).hostname
if AP_CONFIG['blocked_software']:
software = await fetch_nodeinfo(instance)
if software and software.lower() in AP_CONFIG['blocked_software']:
raise aiohttp.web.HTTPUnauthorized(body='relays have been blocked', content_type='text/plain')
if 'actor' not in data or not request['validated']: if 'actor' not in data or not request['validated']:
raise aiohttp.web.HTTPUnauthorized(body='access denied', content_type='text/plain') raise aiohttp.web.HTTPUnauthorized(body='access denied', content_type='text/plain')

View File

@ -12,13 +12,18 @@ CACHE_TTL = CONFIG.get('cache-ttl', 3600)
ACTORS = TTLCache(CACHE_SIZE, CACHE_TTL) ACTORS = TTLCache(CACHE_SIZE, CACHE_TTL)
async def fetch_actor(uri, force=False): async def fetch_actor(uri, headers={}, force=False):
if uri in ACTORS and not force: if uri in ACTORS and not force:
return ACTORS[uri] return ACTORS[uri]
new_headers = {'Accept': 'application/activity+json'}
for k,v in headers.items():
new_headers[k.capitalize()] = v
try: try:
async with aiohttp.ClientSession(trace_configs=[http_debug()]) as session: async with aiohttp.ClientSession(trace_configs=[http_debug()]) as session:
async with session.get(uri, headers={'Accept': 'application/activity+json'}) as resp: async with session.get(uri, headers=new_headers) as resp:
if resp.status != 200: if resp.status != 200:
return None return None
ACTORS[uri] = (await resp.json(encoding='utf-8', content_type=None)) ACTORS[uri] = (await resp.json(encoding='utf-8', content_type=None))

View File

@ -6,7 +6,7 @@ idna==2.7
idna-ssl==1.1.0 idna-ssl==1.1.0
multidict==4.3.1 multidict==4.3.1
pycryptodome==3.9.4 pycryptodome==3.9.4
PyYAML==3.13 PyYAML>=5.1
simplejson==3.16.0 simplejson==3.16.0
yarl==1.2.6 yarl==1.2.6
cachetools cachetools