Tutorial: Adding a blocklist
bovine claims to be modular. This means that it can be configured by adding code. In this tutorial, we will take a look at how to add a domain level block list to bovine store. This will lead all signed requests coming from an actor belonging to these servers to fail. In practice this means, that users on these instances can neither fetch content from bovine_store nor posts to the endpoints defined later.
The blocklist class
The following class defines our blocklist. It contains a list of domains to block.
The check_retriever coroutine extracts the domain from the retriever and
if container in the list, returns an unauthorized response.
from quart import g
from urllib.parse import urlparse
import logging
logger = logging.getLogger(__name__)
class Blocklist:
def __init__(self):
self.entries = ["mastodon.social"]
async def check_retriever(self):
if g.retriever:
domain = urlparse(g.retriever).netloc
if domain in self.entries:
logger.warning("Blocked %s", g.retriever)
return {"status": "unauthorized"}, 401
In practice, one will not want to hard code the blocklist. Either one can load one from a simple text file, containing a list of domains. Another option would to have a table in the database that contains records for known domains. These can then have a blocked status.
Similarly, one has access to the entire request here. So by changing the imports to
one can access the request headers with request.headers and so on.
Adding the blocklist
Adding the blocklist now to a bovine application with bovine_herd would look as follows
from quart import Quart
from bovine_herd import BovineHerd
from bovine_herd.server.authorize import add_authorization
from .blocklist import Blocklist
blocklist = Blocklist()
async def authorize():
result = await add_authorization()
if result:
return result
return await blocklist.check_retriever()
app = Quart(__name__)
BovineHerd(app, authorization_adder=authorize)
This is a somewhat minimal fully functional example.