O Mastodon usa ActivityPub para comunicação entre instâncias. Cada mensagem enviada a outro servidor deve ser assinada com sua chave privada. Sem uma assinatura válida, o servidor rejeita a requisição como não autenticada. Este artigo explica o processo manual de criar e anexar assinaturas HTTP a requisições ActivityPub para federação no Mastodon.
Principais Pontos: Fluxo de Assinatura ActivityPub Manual
- Algoritmo de assinatura RSA-SHA256: Exigido pelo Mastodon para verificar requisições de federação recebidas.
- Headers de assinatura HTTP: Incluem (request-target), host, date e digest.
- Key-ID no header Signature: Deve corresponder exatamente à URL da chave pública do ator.
Por que as Requisições ActivityPub Precisam de Assinatura Manual
ActivityPub é um protocolo descentralizado. Cada instância do Mastodon é um servidor independente. Quando seu servidor envia uma mensagem para outra instância, o servidor receptor precisa confirmar que o remetente é quem diz ser. O padrão HTTP Signature fornece essa autenticação.
A assinatura prova que a requisição veio do ator associado à chave pública. Sem ela, qualquer servidor poderia se passar por um usuário ou outra instância. O Mastodon rejeita requisições não assinadas com código de status HTTP 401 ou 403.
A assinatura manual se torna necessária quando você está construindo um cliente ActivityPub personalizado, testando lógica de federação ou depurando por que as mensagens do seu servidor não são aceitas. Bibliotecas automatizadas como http-signatures ou activitypub-core existem, mas entender o processo manual ajuda a solucionar falhas e implementar o protocolo corretamente.
Passos para Assinar Requisições ActivityPub Manualmente
- Obter a chave privada do ator
Localize a chave privada RSA associada ao ator que está enviando a requisição. O Mastodon armazena essa chave no banco de dados. Para uma implementação personalizada, gere um par de chaves usando OpenSSL:openssl genrsa -out private.pem 2048. Extraia a chave pública no formato PEM:openssl rsa -in private.pem -pubout -out public.pem. - Preparar os headers da assinatura HTTP
O Mastodon exige estes headers na string de assinatura:(request-target),host,dateedigest. O header(request-target)é o método HTTP e o caminho em minúsculas. Por exemplo:post /inbox. O headerhosté o domínio do servidor receptor. O headerdateé o timestamp UTC atual no formato RFC 1123. O headerdigestcontém o hash SHA-256 do corpo da requisição em base64. - Criar a string de assinatura
Junte os valores dos headers em ordem, cada um em uma nova linha com dois pontos e um espaço entre o nome do header e o valor. Exemplo:(request-target): post /inbox
host: remote-instance.social
date: Thu, 15 Jun 2023 12:00:00 GMT
digest: SHA-256=base64hashvalue - Assinar a string com RSA-SHA256
Use a chave privada para assinar a string de assinatura. No OpenSSL:echo -n "string de assinatura" | openssl dgst -sha256 -sign private.pem | openssl base64 -A. A saída é a assinatura codificada em base64. - Construir o header Signature
Construa o header HTTPSignaturecom estes parâmetros:keyId="https://sua-instancia.social/users/username#main-key"algorithm="rsa-sha256"headers="(request-target) host date digest"signature="assinaturabase64"
Separe cada parâmetro por vírgula. OkeyIddeve ser a URL exata da chave pública do ator, conforme exposta no JSON ActivityStreams do ator. - Enviar a requisição HTTP
Inclua o headerSignature, o headerDate, o headerDigeste o corpo da requisição. O Mastodon espera que o corpo seja um documento JSON-LD com um tipo ActivityStreams válido, comoCreate,FollowouAnnounce.
Erros Comuns ao Assinar Requisições ActivityPub
A ordem do header Signature não corresponde ao parâmetro headers
O parâmetro headers no header Signature lista os nomes dos headers na ordem exata em que aparecem na string de assinatura. Se você incluir date antes de host na lista, a string de assinatura também deve listá-los nessa mesma ordem. O Mastodon verifica a assinatura reconstruindo a string usando a ordem declarada. Uma incompatibilidade causa falha na verificação da assinatura.
Formato incorreto da URL keyId
O keyId deve apontar para a chave pública incorporada na representação JSON do ator. Um erro comum é usar a URL do perfil do ator sem o fragmento #main-key. O Mastodon consulta a URL do keyId e espera encontrar um objeto publicKey com um campo publicKeyPem. Use a URL exata que o JSON do ator expõe.
Header date muito distante do horário do servidor
O Mastodon permite uma diferença máxima de 30 segundos entre o header Date e o horário atual do servidor receptor. Se o relógio do seu servidor estiver com uma diferença maior que 30 segundos, a requisição é rejeitada. Sincronize o horário do seu servidor usando NTP. Alternativamente, inclua o header (created) na string de assinatura para usar um timestamp mais flexível.
| Item | Usando Biblioteca Automatizada | Assinatura Manual |
|---|---|---|
| Esforço de implementação | Mínimo, a biblioteca lida com hashing e construção de headers | Requer construção manual passo a passo de cada header e assinatura |
| Visibilidade de depuração | Limitada, a biblioteca abstrai o processo de assinatura | Controle total sobre cada header e parâmetro, mais fácil isolar falhas |
| Taxa de erro | Menor, a biblioteca segue a especificação exatamente | Maior, um dois pontos mal colocado ou ordem errada de headers quebra a assinatura |
| Flexibilidade para headers personalizados | Depende do suporte da biblioteca para listas de headers personalizadas | Liberdade total para incluir qualquer header na string de assinatura |
A assinatura manual é melhor para testes, depuração e aprendizado do protocolo ActivityPub. Para sistemas em produção, use uma biblioteca bem testada para reduzir erros.
Agora você pode construir e anexar assinaturas HTTP a requisições ActivityPub para federação no Mastodon. Comece testando com uma atividade Follow simples para uma instância de teste. Verifique a assinatura usando o endpoint de depuração do Mastodon ou verificando os logs do servidor em busca de erros 401. Para depuração avançada, use uma ferramenta como ngrok para inspecionar os headers exatos que seu servidor envia e compará-los com o formato esperado.