danirod.es

Antes que nada, lo siento

Mastodon me tumba el blog

📁

Mastodon puede causar un DDoS, pero no lo hace con mala intención.

He vuelto a cometer el error de publicar una entrada de blog usando una etiqueta demasiado genérica. Al minuto de hacer eso, mi blog ha empezado a fallar, y el access.log se ha llenado de tráfico procedente de instancias de Mastodon.

NGINX recibiendo un DDoS de Mastodon

Temporalmente voy a apagar los hashtags en la integración que uso para conectar este blog al fediverso, porque esto va a reducir su presencia en los buscadores de etiquetas. Aprovecharé la ocasión también para revisar si tengo demasiadas etiquetas o si puedo fusionar algunas para clasificar mejor lo que escribo.

¿Por qué imagino que pasa esto?

Cuando publico un post, ese post es enviado al fediverso, así que es enviado a las instancias que me siguen. En principio esa operación es unidireccional. Es mi lado quien mira quién me sigue y contacta con sus servidores para anunciarles que he escrito algo.

Sin embargo, si el software que usa la instancia que recibe mi post muestra tarjetas de vista previa, como Mastodon, tendrá que hacer una petición extra a la página web para traerse los metadatos Open Graph y así construir su tarjeta de vista previa. Eso ya es 1 petición entrante.

Desde la versión 4.3 de Mastodon, hay un nuevo metadato llamado fediverse:creator, que permite poner junto a la propia tarjeta de vista previa, un enlace al perfil de fediverso de la persona que ha escrito el artículo. Perdón por la referencia cruzada, pero esto ya lo dije en Nos Gusta Linux. Por lo tanto, imagino que un nodo de Mastodon que use la versión 4.3, al ver que este sitio web tiene una etiqueta apuntando a @dani@danirod.es, tendrá que extraer también información de esa cuenta.

Eso explicaría por qué en el log veo tantas peticiones atacando al endpoint de webfinger de esta web (o sea, /.well-known/webfinger?resource=acct:dani@danirod.es). La vez anterior que me hice un DDoS por esta misma razón, no estaban esas peticiones, pero Mastodon 4.3 todavía no había salido, así que me parece una explicación plausible.

Pero el webfinger todo lo que le entrega a la integración es la URL de la que debe sacar el perfil en formato JSON-LD, para importarlo o actualizarlo en su sistema. Por eso veo tantas peticiones a https://danirod.es/author/dani/, porque esa es la URL que contiene la información JSON-LD. Con esto ya van 3 peticiones como mínimo.

Todo eso por encima de que existen otras URLs, como https://danirod.es/wp-json/activitypub/1.0/actors/1/collections/featured o https://danirod.es/wp-json/activitypub/1.0/actors/1/followers. Si una instancia quiere saber cuántos seguidores tengo o cuántos son locales, para agregarlo a su sistema local, eso son más peticiones que hacer.

Es bueno y malo

Soy muy fan de las APIs HTTP bien hechas, y eso incluye cosas como usar la negociación de contenido, o que cada recurso tenga su propia URL conectada con enlaces.

Desde este punto de vista, creo que lo que está haciendo ActivityPub aquí es perfecto. Cada URL representa una cosa, y si quieres conocer todo, vas a tener que hacer varias peticiones. Me gusta diseñar mis APIs así, y aunque si tengo que tragarme el sapo y optimizar el diseño de mi API porque lo requiere mi trabajo lo voy a hacer, a nivel ideológico mi API perfecta también funcionaría así.

Sin embargo, tenemos que aceptar que eso implica hacer varias peticiones. Y que cuando se trata de un sistema federado y distribuido como es el fediverso, eso son múltiples clientes a la vez haciendo esas peticiones.

Dani, tienes que usar caché

Bueno, ya uso WP Supercache en mi sitio web, así que gracias por la opinión. Nunca despliego un WordPress sin instalar el Supercache.

El problema es que los softwares para fediverso tradicionales no se suelen llevar bien con las caches. El primer problema es que el protocolo ActivityPub utiliza mucho elementos de HTTP como la negociación de contenido o la firma de peticiones HTTP, por lo que en muchas ocasiones simplemente no es viable debido a la complejidad de una petición. Esa es la razón por la que si visitas https://danirod.es/author/dani/ con tu navegador web, ves la lista de posts en formato HTML, pero si usas curl o un programa para probar APIs como Bruno o Postman para pedir la misma URL usando la cabecera Accept: application/ld+json, ves un JSON, sin cambiar la URL.

Incluso podría ocurrir que una cache sirva información desactualizada entre un endpoint y otro, lo que podría causar problemas de sincronización para las instancias receptoras. Por supuesto, esas instancias están más que invitadísimas a guardar en una base de datos la respuesta que reciben para no tener que pedirlo en un tiempo, pero tiene que hacerse todo a la vez.

Además, ActivityPub usa mucho el verbo HTTP POST para transferir información entre un servidor y otro. Al fin y al cabo, ActivityPub no son más que webhooks donde un servidor habla con otro para enviar nuevos posts, actualizar datos o simplemente actuar en nombre de sus usuarios. Y el verbo HTTP POST no se puede cachear, porque así es como funcionan las normas sobre idempotencia en HTTP, punto.

Por lo tanto, no sé si es tan fácil como intentar envolver el plugin de ActivityPub en una caché, pero es algo que definitivamente debería mirar.


(¿Cómo dar me gusta o repostear?)

Puedes dar repost o like a esta publicación desde el fediverso. Pon la URL del artículo en el buscador de tu instancia de fediverso (por ejemplo, Mastodon), y haz una búsqueda. Lo normal es que tu instancia haga un descubrimiento del post que hay en la URL y aparezca como primer resultado.