Digest header / federation

I recently set up a Mastodon server on a Debian 10 container on my local system by following the tutorial on this site at:

Now I’m trying to send an ActivityPub message to my Mastodon server following the tutorial at:

But when I send the request, the response I am receiving states:
“Mastodon requires the Digest header to be signed when doing a POST request”

Based on a few later posts on this site, it looks like I need to include an additional Digest header that may not have been required when that tutorial was written:

[Trimmed due to “new user” limitations; will try to continue in a reply]

I’ve updated the deliver.rb script to attempt to send this digest (formatted as described at litepub . social/overview) but it isn’t working – it still returns an error saying that the digest header is missing:

require 'http'
require 'openssl'

document      = File.read('create-hello-world.json')
date          = Time.now.utc.httpdate
keypair       = OpenSSL::PKey::RSA.new(File.read('private.pem'))
signed_string = "(request-target): post /inbox\nhost: mastodon.urza9814.com\ndate: #{date}"
signature     = Base64.strict_encode64(keypair.sign(OpenSSL::Digest::SHA256.new, signed_string))
header        = 'keyId="https://www.urza9814.com/actor",headers="(request-target) host date",signature="' + signature + '"'
print header+"\n"
digest = Base64.strict_encode64(keypair.sign(OpenSSL::Digest::SHA256.new, document))

signed_string = "test"
print "Test string: "+Base64.strict_encode64(keypair.sign(OpenSSL::Digest::SHA256.new, signed_string))+"\n"

print HTTP.headers({ 'Host': 'mastodon.urza9814.com', 'Date': date, 'Signature': header, 'Digest': "SHA-256="+digest })
    .post('https://mastodon.urza9814.com/inbox', body: document)

[root@www aptest]# ruby deliver.rb 
Mastodon requires the Digest header to be signed when doing a POST request

[ Trimmed due to “new user” limitations, will continue in next reply]

I put my Mastodon server into debug logging mode, but the only message I see in the logs is a 401 error:

Jan 30 17:58:38 mastodon bundle[9967]: [ce320cd9-6da9-484e-b18a-a1c12c75e2af] method=POST path=/inbox format=html controller=ActivityPub::InboxesController action=create status=401 duration=8.16 view=0.21 key=https://www.urza9814.com/actor

[Trimmed due to “new user” limitations, will continue in next reply]

But I’m not sure where that 401 is coming from because I can access that URL without issue using wget:

root@mastodon:~# wget https://www.urza9814.com/actor
--2021-01-30 17:59:54--  https://www.urza9814.com/actor

[Trimmed due to “new user” limitations, will continue in next reply]

Resolving www.urza9814.com (www.urza9814.com)... 192.168.1.34
Connecting to www.urza9814.com (www.urza9814.com)|192.168.1.34|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 862
Saving to: 'actor'

actor                                       100%[=========================================================================================>]     862  --.-KB/s    in 0s      

2021-01-30 17:59:56 (17.0 MB/s) - 'actor' saved [862/862]



root@mastodon:~# cat actor
{
	"@context": [
		"https://www.w3.org/ns/activitystreams",
		"https://w3id.org/security/v1"
	],

	"id": "https://www.urza9814.com/actor",
	"type": "Person",
	"preferredUsername": "Urza9814",
	"inbox": "https://www.urza9814.com/inbox",

	"publicKey": {
		"id": "https://www.urza9814.com/actor#main-key",
		"owner": "https://www.urza9814.com/actor",
		"publicKeyPem": "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1MFF5JcUZN2t3HeRgpYTlFqHVSau9YJGmlRxqfcbtKlshaZTMNSO3Diko0Xnb8U42IBd+hJF0I+O+Jg48GFQG6xavmNxTiJS8dCc7i3BTaZxMUY985xa8ijqppRbrzsLXdD28QTNVS7FpnBwfq7zz0FDNSyrR4UA1oiCZsKcixEtaZRPahRBMu4ptOd7i5CjhRPZ+40D4cxPLl7jfxeLy2n7Q3kKOAYul8vc83KkIbXSYITn18gkWUzwvrUB+vTPHKw+dmjHIiV9xhL+4n/ROi+T2NwxffKgwAbScdIYUX8ThE78I2Yrt+1CYzBU/hhK5gu8kQuWni2iRklzY3YOtwIDAQAB-----END PUBLIC KEY-----"
	}
}

And the IP of that web server is added in the ALLOWED_PRIVATE_ADDRESSES in mastodon’s .env.production file. Nothing is logged in the web server logs. I do get a 401 logged in the nginx logs on the Mastodon server:

192.168.1.34 - - [30/Jan/2021:18:03:03 +0000] "POST /inbox HTTP/1.1" 401 85 "-" "http.rb/4.4.1"

I also did a sanity check by configuring a second mastodon server, and those two servers can’t communicate either so there may be some deeper issue. After I added the servers to each others’ ALLOWED_PRIVATE_ADDRESSES list, they admin accounts can find each other in the search box if I enter the full username, and I see the user photo show up, and I can send a follow request or send a direct message…but none of those ever appear from the other user’s account. I don’t see any errors in the logs when I try to send messages between the two servers. One account is locked, the other is not, but either way they show their own requests as pending but from the other server no request every shows up, no message ever shows up, everything seems to just silently fail!

Neither server is currently accessible from the outside internet, but as I understand it that shouldn’t be an issue. They can communicate on the local network, and they do have valid SSL certs (from lets encrypt, although I generate those through a pfsense plugin rather than the commands in the tutorial). Both Mastodon servers are the same version, same container template, installed following the same tutorial only one day apart.

(Sorry about the many posts, but I figure I’ll either get yelled at for making many posts or I’ll get yelled at for not including the actual JSON and error messages…ya can’t win with this three url policy :wink: )

These may be helpful:

Hmm…that says parameters don’t need to be quoted strings, but if I remove the quotes it returns “Error parsing signature parameters”…

I did make a bit of progress though. The spec linked to from that first post actually returns a 404, but it was close enough to get me to this:
https://tools.ietf.org/id/draft-cavage-http-signatures-10.html

So I got the digest calculating correctly now with the following code:

require 'http'
require 'openssl'

document      = File.read('create-hello-world.json')
date          = Time.now.utc.httpdate
keypair       = OpenSSL::PKey::RSA.new(File.read('private.pem'))
digest        = Base64.strict_encode64((OpenSSL::Digest::SHA256.new).digest(document))
signed_string = "(request-target): post /inbox\nhost: mastodon.urza9814.com\ndate: #{date}\ndigest: SHA-256=#{digest}"
signature     = Base64.strict_encode64(keypair.sign(OpenSSL::Digest::SHA256.new, signed_string))
header        = 'keyId="https://www.urza9814.com/actor",headers="(request-target) host date digest",signature="' + signature + '"'

print HTTP.headers({ 'Host': 'mastodon.urza9814.com', 'Date': date, 'Signature': header, 'digest': "SHA-256="+digest })
    .post('https://mastodon.urza9814.com/inbox', body: document)
print "\n"

That was still giving me an error:

Public key not found for key https://www.urza9814.com/actor

I tried regenerating the keys, tried changing the format (with/without \n or newlines, that sort of thing)…but I noticed in the logs that it was trying to hit /inbox on my web server, so I created a zero-byte inbox file and the error went away. I still don’t have anything showing up on the server though…just a 202 response in the log which seems…odd:

Jan 31 20:41:57 mastodon bundle[15571]: [e0426ec7-29d3-4134-8497-8c24aec9b4cc] method=POST path=/inbox format=html controller=ActivityPub::InboxesController action=create status=202 duration=18.47 view=0.00 db=2.33 key=https://www.urza9814.com/actor

Looks like the 202 might indicate some kind of validation error…although this page also seems to indicate that the 202 responses were removed years ago, and my server is running v3.3.0:

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.