[bug] 500 internal server error when federating Like activity from Bridgy Fed #438

Open
opened 2023-01-18 23:07:25 +00:00 by snarfed · 66 comments

Details

tested against akko.wtf, not my own instance

Version

3.5.0-12-g63f2d1cb

PostgreSQL version

No response

What were you trying to do?

Hi! I tried federating a Like from Bridgy Fed to this post on akko.wtf (running backend v3.5.0-12-g63f2d1cb), and it failed. Details below. Also tracking here. Not urgent, thanks in advance for looking!

What did you expect to happen?

HTTP 200 or 202 on the inbox delivery request to https://akko.wtf/users/rei/inbox.

What actually happened?

HTTP 500 error with body {"errors":{"detail":"Internal server error"}}.

This is the same error we got from Pleroma, so it's probably activity handling code that hasn't changed since the fork. I'm guessing it choked on some part of the AS2 that's a composite object when it expects a string, maybe actor.

Bridgy Fed log here. Full AS2 object we delivered is below.

Logs

{
  "published": "2023-01-18T14:54:09-08:00",
  "content": "likes <a class=\"u-like u-like-of\" href=\"https://akko.wtf/notice/ARP7mWTNZxbJAzPTIe\">Luna Nova</a>",
  "url": "https://fed.brid.gy/r/https://snarfed.org/2023-01-18_luna-nova",
  "actor": {
    "url": "https://fed.brid.gy/r/https://snarfed.org/",
    "image": {
      "url": "https://secure.gravatar.com/avatar/947b5f3f323da0ef785b6f02d9c265d6?s=96&d=blank&r=g",
      "type": "Image"
    },
    "type": "Person",
    "name": "Ryan Barrett",
    "icon": {
      "url": "https://secure.gravatar.com/avatar/947b5f3f323da0ef785b6f02d9c265d6?s=96&d=blank&r=g",
      "type": "Image"
    },
    "id": "https://fed.brid.gy/snarfed.org",
    "preferredUsername": "snarfed.org"
  },
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Like",
  "object": "https://akko.wtf/objects/8044263c-b1d3-495a-9a43-131bee571c80",
  "id": "https://fed.brid.gy/r/https://snarfed.org/2023-01-18_luna-nova",
  "cc": [
    "https://akko.wtf/users/rei",
    "https://www.w3.org/ns/activitystreams#Public",
    "https://akko.wtf/users/rei/followers"
  ],
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ]
}

Severity

I cannot use the software

Have you searched for this issue?

  • I have double-checked and have not found this issue mentioned anywhere.
### Details tested against akko.wtf, not my own instance ### Version 3.5.0-12-g63f2d1cb ### PostgreSQL version _No response_ ### What were you trying to do? Hi! I tried federating a `Like` from [Bridgy Fed](https://fed.brid.gy/) to [this post](https://akko.wtf/notice/ARP7mWTNZxbJAzPTIe) on akko.wtf (running backend v[3.5.0-12-g63f2d1cb](https://akkoma.dev/AkkomaGang/akkoma/commit/63f2d1cb)), and it failed. Details below. [Also tracking here.](https://github.com/snarfed/bridgy-fed/issues/374) Not urgent, thanks in advance for looking! ### What did you expect to happen? HTTP 200 or 202 on the inbox delivery request to `https://akko.wtf/users/rei/inbox`. ### What actually happened? HTTP 500 error with body `{"errors":{"detail":"Internal server error"}}`. This is the [same error we got from Pleroma](https://github.com/snarfed/bridgy-fed/issues/12#issuecomment-1385812088), so it's probably activity handling code that hasn't changed since the fork. I'm guessing it choked on some part of the AS2 that's a composite object when it expects a string, maybe `actor`. [Bridgy Fed log here.](https://fed.brid.gy/log?start_time=1674082460&key=https://snarfed.org/2023-01-18_luna-nova%20https://akko.wtf/objects/8044263c-b1d3-495a-9a43-131bee571c80&module=) Full AS2 object we delivered is below. ### Logs ```json { "published": "2023-01-18T14:54:09-08:00", "content": "likes <a class=\"u-like u-like-of\" href=\"https://akko.wtf/notice/ARP7mWTNZxbJAzPTIe\">Luna Nova</a>", "url": "https://fed.brid.gy/r/https://snarfed.org/2023-01-18_luna-nova", "actor": { "url": "https://fed.brid.gy/r/https://snarfed.org/", "image": { "url": "https://secure.gravatar.com/avatar/947b5f3f323da0ef785b6f02d9c265d6?s=96&d=blank&r=g", "type": "Image" }, "type": "Person", "name": "Ryan Barrett", "icon": { "url": "https://secure.gravatar.com/avatar/947b5f3f323da0ef785b6f02d9c265d6?s=96&d=blank&r=g", "type": "Image" }, "id": "https://fed.brid.gy/snarfed.org", "preferredUsername": "snarfed.org" }, "@context": "https://www.w3.org/ns/activitystreams", "type": "Like", "object": "https://akko.wtf/objects/8044263c-b1d3-495a-9a43-131bee571c80", "id": "https://fed.brid.gy/r/https://snarfed.org/2023-01-18_luna-nova", "cc": [ "https://akko.wtf/users/rei", "https://www.w3.org/ns/activitystreams#Public", "https://akko.wtf/users/rei/followers" ], "to": [ "https://www.w3.org/ns/activitystreams#Public" ] } ``` ### Severity I cannot use the software ### Have you searched for this issue? - [x] I have double-checked and have not found this issue mentioned anywhere.
snarfed added the
bug
label 2023-01-18 23:07:25 +00:00

my guess would be that the usernames are failing validation since they contain restricted characters, and thus failing the http signature check

I'll have to check , but a rejection would probably be the correct behaviour

my guess would be that the usernames are failing validation since they contain restricted characters, and thus failing the http signature check I'll have to check , but a rejection would probably be the correct behaviour
Author

Hah, true! Afaik neither the AS2 core nor vocab specs define allowed or restricted characters for preferredUsername, but regardless, you're right, that Markdown value is clearly wrong. Good eyes, thanks for the catch, I'll fix and try again.

Hah, true! Afaik neither the AS2 [core](https://www.w3.org/TR/activitystreams-core/) nor [vocab](https://www.w3.org/TR/activitystreams-core/) specs define allowed or restricted characters for `preferredUsername`, but regardless, you're right, that Markdown value is clearly wrong. Good eyes, thanks for the catch, I'll fix and try again.
Author

Ah, my mistake, that was a copy paste artifact from the Bridgy Fed log where it got auto-linked. The actual preferredUsername value was just snarfed.org. I've updated the activity JSON in the issue here.

Ah, my mistake, that was a copy paste artifact from the [Bridgy Fed log](https://fed.brid.gy/log?start_time=1674082460&key=https://snarfed.org/2023-01-18_luna-nova%20https://akko.wtf/objects/8044263c-b1d3-495a-9a43-131bee571c80&module=) where it got auto-linked. The actual `preferredUsername` value was just `snarfed.org`. I've updated the activity JSON in the issue here.
Contributor

I tried debugging, but got stuck :( Here's what I did in case someone else wants to debug further:

  1. I can fetch the actor fine using pleroma-fe search, so the actor is OK (see screenshot)
  2. When fetching the Like by id, it has a correct content-type content-type: application/activity+json
    • curl -v 'https://fed.brid.gy/r/https://snarfed.org/2023-01-18_luna-nova' -H 'accept: application/activity+json' | jq .
  3. We can't debug trying to fetch the Like activity through the search, because apparently Akkoma can't fetch a Like that way :( I tried with a like from my own instance and that didn't work either. I don't know why Akkoma doesn't allow this, it works for actors and things like Article or Note.
  4. I tried adding a test[1]. I changed some id's to make the test not fail on signature or containment with the set-up I did, but I don't think I changed something so fundamental to the object that it would behave differently. The test passed, however, so it's still unclear why sending the Like doesn't work. Maybe the set-up could be done better to keep true to the actual example we have here, but I'm not really expecting a different result.

Next thing I guess is to see if we can trigger a Like from a Birdy Fed instance and follow in Akkoma what happens with it, but at first glance, that seems more involved than simply making an account and pressing a like button (pls tell me if I'm wrong, or what would be the easiest way to try this).

[1] Click to expand I add the following test to test/pleroma/web/activity_pub/activity_pub_controller_test.exs under `describe "/inbox"`. Note that I change the object and actor to ones who I added first in the test. The object shouldn't matter, and I assume the actor also isn't the problem since we can properly fetch that one.

Note that I change the actor id, but keep it an object.

    test "it inserts an incoming Like activity from birdy fed", %{conn: conn} do
      note_activity = insert(:note_activity)
      note_object = Object.normalize(note_activity, fetch: false)
      user = User.get_cached_by_ap_id(note_activity.data["actor"])

      data =
        File.read!("test/fixtures/fedi-birdyfed-like-activity.json")
        |> Jason.decode!()
        |> Map.put("object", note_object.data["id"])

      data =
        data
        |> Map.put("actor", data["actor"] |> Map.put("id", user.ap_id))
        |> IO.inspect(label: "Like")

      conn =
        conn
        |> assign(:valid_signature, true)
        |> put_req_header(
          "signature",
          "keyId=\"#{user.ap_id}/main-key\""
        )
        |> put_req_header("content-type", "application/activity+json")
        |> post("/inbox", data)

      assert "ok" == json_response(conn, 200)

      ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
      assert Activity.get_by_ap_id(data["id"])
    end

test/fixtures/fedi-birdyfed-like-activity.json has the following content I got from the OP (I also tried with the content I fetched with curl 'https://fed.brid.gy/r/https://snarfed.org/2023-01-18_luna-nova' -H 'accept: application/activity+json'). Note that I changed the id to use domain http://localhost:4001. This is because we otherwise get a containment error.

{
  "published": "2023-01-18T14:54:09-08:00",
  "content": "likes <a class=\"u-like u-like-of\" href=\"https://akko.wtf/notice/ARP7mWTNZxbJAzPTIe\">Luna Nova</a>",
  "url": "https://fed.brid.gy/r/https://snarfed.org/2023-01-18_luna-nova",
  "actor": {
    "url": "https://fed.brid.gy/r/https://snarfed.org/",
    "image": {
      "url": "https://secure.gravatar.com/avatar/947b5f3f323da0ef785b6f02d9c265d6?s=96&d=blank&r=g",
      "type": "Image"
    },
    "type": "Person",
    "name": "Ryan Barrett",
    "icon": {
      "url": "https://secure.gravatar.com/avatar/947b5f3f323da0ef785b6f02d9c265d6?s=96&d=blank&r=g",
      "type": "Image"
    },
    "id": "https://fed.brid.gy/snarfed.org",
    "preferredUsername": "snarfed.org"
  },
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Like",
  "object": "https://akko.wtf/objects/8044263c-b1d3-495a-9a43-131bee571c80",
  "id": "http://localhost:4001/r/https://snarfed.org/2023-01-18_luna-nova",
  "cc": [
    "https://akko.wtf/users/rei",
    "https://www.w3.org/ns/activitystreams#Public",
    "https://akko.wtf/users/rei/followers"
  ],
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ]
}```
</details>
I tried debugging, but got stuck :( Here's what I did in case someone else wants to debug further: 1. I can fetch the actor fine using pleroma-fe search, so the actor is OK (see screenshot) 2. When fetching the Like by id, it has a correct content-type `content-type: application/activity+json` * `curl -v 'https://fed.brid.gy/r/https://snarfed.org/2023-01-18_luna-nova' -H 'accept: application/activity+json' | jq .` 3. We can't debug trying to fetch the Like activity through the search, because apparently Akkoma can't fetch a Like that way :( I tried with a like from my own instance and that didn't work either. I don't know why Akkoma doesn't allow this, it works for actors and things like Article or Note. 4. I tried adding a test[1]. I changed some id's to make the test not fail on signature or containment with the set-up I did, but I don't think I changed something so fundamental to the object that it would behave differently. The test passed, however, so it's still unclear why sending the Like doesn't work. Maybe the set-up could be done better to keep true to the actual example we have here, but I'm not really expecting a different result. Next thing I guess is to see if we can trigger a Like from a Birdy Fed instance and follow in Akkoma what happens with it, but at first glance, that seems more involved than simply making an account and pressing a like button (pls tell me if I'm wrong, or what would be the easiest way to try this). <details> <summary>[1] Click to expand</summary> I add the following test to test/pleroma/web/activity_pub/activity_pub_controller_test.exs under `describe "/inbox"`. Note that I change the object and actor to ones who I added first in the test. The object shouldn't matter, and I assume the actor also isn't the problem since we can properly fetch that one. Note that I change the actor id, but keep it an object. ``` test "it inserts an incoming Like activity from birdy fed", %{conn: conn} do note_activity = insert(:note_activity) note_object = Object.normalize(note_activity, fetch: false) user = User.get_cached_by_ap_id(note_activity.data["actor"]) data = File.read!("test/fixtures/fedi-birdyfed-like-activity.json") |> Jason.decode!() |> Map.put("object", note_object.data["id"]) data = data |> Map.put("actor", data["actor"] |> Map.put("id", user.ap_id)) |> IO.inspect(label: "Like") conn = conn |> assign(:valid_signature, true) |> put_req_header( "signature", "keyId=\"#{user.ap_id}/main-key\"" ) |> put_req_header("content-type", "application/activity+json") |> post("/inbox", data) assert "ok" == json_response(conn, 200) ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) assert Activity.get_by_ap_id(data["id"]) end ``` test/fixtures/fedi-birdyfed-like-activity.json has the following content I got from the OP (I also tried with the content I fetched with `curl 'https://fed.brid.gy/r/https://snarfed.org/2023-01-18_luna-nova' -H 'accept: application/activity+json'`). Note that I changed the id to use domain `http://localhost:4001`. This is because we otherwise get a containment error. ``` { "published": "2023-01-18T14:54:09-08:00", "content": "likes <a class=\"u-like u-like-of\" href=\"https://akko.wtf/notice/ARP7mWTNZxbJAzPTIe\">Luna Nova</a>", "url": "https://fed.brid.gy/r/https://snarfed.org/2023-01-18_luna-nova", "actor": { "url": "https://fed.brid.gy/r/https://snarfed.org/", "image": { "url": "https://secure.gravatar.com/avatar/947b5f3f323da0ef785b6f02d9c265d6?s=96&d=blank&r=g", "type": "Image" }, "type": "Person", "name": "Ryan Barrett", "icon": { "url": "https://secure.gravatar.com/avatar/947b5f3f323da0ef785b6f02d9c265d6?s=96&d=blank&r=g", "type": "Image" }, "id": "https://fed.brid.gy/snarfed.org", "preferredUsername": "snarfed.org" }, "@context": "https://www.w3.org/ns/activitystreams", "type": "Like", "object": "https://akko.wtf/objects/8044263c-b1d3-495a-9a43-131bee571c80", "id": "http://localhost:4001/r/https://snarfed.org/2023-01-18_luna-nova", "cc": [ "https://akko.wtf/users/rei", "https://www.w3.org/ns/activitystreams#Public", "https://akko.wtf/users/rei/followers" ], "to": [ "https://www.w3.org/ns/activitystreams#Public" ] }``` </details>

yeah I did some similar testing, to the same result

whatever is happening, it's happening during http sig verification

and bridgy is... not the easiest thing to get a test instance of

yeah I did some similar testing, to the same result whatever is happening, it's happening during http sig verification and bridgy is... not the easiest thing to get a test instance of
Author

Thanks for all the sleuthing, sorry this is hard to test! I'm happy to federate another like from Bridgy Fed whenever you want.

Thanks for all the sleuthing, sorry this is hard to test! I'm happy to federate another like from Bridgy Fed whenever you want.

sending the content of the Like with signature verification off ends up with it processing just fine, so yea it's 100% in sigs

now to try and isolate the part of bridgy that does that, this will be fun

sending the content of the `Like` with signature verification off ends up with it processing just fine, so yea it's 100% in sigs now to try and isolate the part of bridgy that does that, this will be fun
Author
https://github.com/snarfed/bridgy-fed/blob/af769de99eec84039590d9c1fad3326849449048/common.py#L120-L151

hm, after a rather... painful time trying to extract it, it seems to process fine :<<<

i'm going to have to debug this in prod aren't i

hm, after a rather... painful time trying to extract it, it seems to process fine :<<< i'm going to have to debug this in prod aren't i
Contributor

More examples of this 🤷🏻‍♀️

[More examples](https://github.com/snarfed/bridgy-fed/issues/651) of this 🤷🏻‍♀️
Contributor

And, I don't know if it's the same but similar issues in Tootik.

And, I don't know if it's the same [but similar issues in Tootik](https://github.com/dimkr/tootik/issues/17).
Author

Hi! Sorry this hasn't been easier to debug. Happy to help if I can!

New example activity below that https://idiomdrottning.org/users/Sandra/inbox (@snan which Akkoma version?) 500ed on just now. Often this is because there's a full object (which is still valid AS2/AP) in a field where the receiving server (Akkoma here) expects just an id, eg attributedTo below. Not sure if that's contributing here.

{
  "id": "https://fed.brid.gy/r/https://snarfed.org/2023-09-26_sandra-cw-inappropriately-named-term-indieweb-social#bridgy-fed-create",
  "actor": "https://fed.brid.gy/snarfed.org",
  "published": "2023-09-26T20:19:22.766584+00:00",
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Create",
  "object": {
    "published": "2023-09-26T13:05:02-07:00",
    "content": "Yes! I know NIPs well. The key difference is that anyone can publish a FEP, but NIPs can only be merged into the \u201cblessed\u201d set on <a href=\"https://github.com/nostr-protocol/nips\">github.com/nostr-protocol/nips</a>; by a committer on that repo.\n<p>fiatjaf et al are well aware of that bottleneck though, and they hope to remove it! Click through ^ to the actual table and follow its links for more info, eg <a href=\"https://github.com/nostr-protocol/nips/issues/162\">nostr-protocol/nips#162</a>.</p>\n<p><a class=\"u-in-reply-to\" href=\"https://indieweb.social/@Sandra@idiomdrottning.org/111132295475418675\">\u00a0</a></p>",
    "url": "https://fed.brid.gy/r/https://snarfed.org/2023-09-26_sandra-cw-inappropriately-named-term-indieweb-social",
    "id": "https://fed.brid.gy/r/https://snarfed.org/2023-09-26_sandra-cw-inappropriately-named-term-indieweb-social",
    "type": "Note",
    "attributedTo": {
      "url": "https://fed.brid.gy/r/https://snarfed.org/",
      "image": {
        "url": "https://secure.gravatar.com/avatar/947b5f3f323da0ef785b6f02d9c265d6?s=96&d=blank&r=g",
        "type": "Image"
      },
      "id": "https://fed.brid.gy/snarfed.org",
      "type": "Person",
      "name": "Ryan Barrett",
      "icon": {
        "url": "https://secure.gravatar.com/avatar/947b5f3f323da0ef785b6f02d9c265d6?s=96&d=blank&r=g",
        "type": "Image"
      },
      "inbox": "https://fed.brid.gy/snarfed.org/inbox",
      "outbox": "https://fed.brid.gy/snarfed.org/outbox",
      "preferredUsername": "snarfed.org"
    },
    "inReplyTo": "https://idiomdrottning.org/objects/a91f40bc-7d46-4cae-9be8-665cec8a554a",
    "cc": [
      "https://idiomdrottning.org/users/Sandra",
      "https://idiomdrottning.org/users/Sandra/followers",
      "https://indieweb.social/users/tchambers",
      "https://www.w3.org/ns/activitystreams#Public"
    ],
    "tag": [
      {
        "type": "Mention",
        "href": "https://idiomdrottning.org/users/Sandra"
      },
      {
        "type": "Mention",
        "href": "https://idiomdrottning.org/users/Sandra"
      }
    ],
    "to": [
      "https://www.w3.org/ns/activitystreams#Public"
    ]
  },
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ]
}
Hi! Sorry this hasn't been easier to debug. Happy to help if I can! New example activity below that https://idiomdrottning.org/users/Sandra/inbox (@snan which Akkoma version?) 500ed on just now. Often this is because there's a full object (which is still valid AS2/AP) in a field where the receiving server (Akkoma here) expects just an id, eg `attributedTo` below. Not sure if that's contributing here. ```json { "id": "https://fed.brid.gy/r/https://snarfed.org/2023-09-26_sandra-cw-inappropriately-named-term-indieweb-social#bridgy-fed-create", "actor": "https://fed.brid.gy/snarfed.org", "published": "2023-09-26T20:19:22.766584+00:00", "@context": "https://www.w3.org/ns/activitystreams", "type": "Create", "object": { "published": "2023-09-26T13:05:02-07:00", "content": "Yes! I know NIPs well. The key difference is that anyone can publish a FEP, but NIPs can only be merged into the \u201cblessed\u201d set on <a href=\"https://github.com/nostr-protocol/nips\">github.com/nostr-protocol/nips</a>; by a committer on that repo.\n<p>fiatjaf et al are well aware of that bottleneck though, and they hope to remove it! Click through ^ to the actual table and follow its links for more info, eg <a href=\"https://github.com/nostr-protocol/nips/issues/162\">nostr-protocol/nips#162</a>.</p>\n<p><a class=\"u-in-reply-to\" href=\"https://indieweb.social/@Sandra@idiomdrottning.org/111132295475418675\">\u00a0</a></p>", "url": "https://fed.brid.gy/r/https://snarfed.org/2023-09-26_sandra-cw-inappropriately-named-term-indieweb-social", "id": "https://fed.brid.gy/r/https://snarfed.org/2023-09-26_sandra-cw-inappropriately-named-term-indieweb-social", "type": "Note", "attributedTo": { "url": "https://fed.brid.gy/r/https://snarfed.org/", "image": { "url": "https://secure.gravatar.com/avatar/947b5f3f323da0ef785b6f02d9c265d6?s=96&d=blank&r=g", "type": "Image" }, "id": "https://fed.brid.gy/snarfed.org", "type": "Person", "name": "Ryan Barrett", "icon": { "url": "https://secure.gravatar.com/avatar/947b5f3f323da0ef785b6f02d9c265d6?s=96&d=blank&r=g", "type": "Image" }, "inbox": "https://fed.brid.gy/snarfed.org/inbox", "outbox": "https://fed.brid.gy/snarfed.org/outbox", "preferredUsername": "snarfed.org" }, "inReplyTo": "https://idiomdrottning.org/objects/a91f40bc-7d46-4cae-9be8-665cec8a554a", "cc": [ "https://idiomdrottning.org/users/Sandra", "https://idiomdrottning.org/users/Sandra/followers", "https://indieweb.social/users/tchambers", "https://www.w3.org/ns/activitystreams#Public" ], "tag": [ { "type": "Mention", "href": "https://idiomdrottning.org/users/Sandra" }, { "type": "Mention", "href": "https://idiomdrottning.org/users/Sandra" } ], "to": [ "https://www.w3.org/ns/activitystreams#Public" ] }, "to": [ "https://www.w3.org/ns/activitystreams#Public" ] } ```
Contributor

The version I was running when these requests happened, and still am running as I am writing this, is git commit ebfb617b26 probably better known as one commit after the v3.10.4 tag. (It was the head of stable when last I recompiled.)

@snarfed, could it be some signature issue? That's another area where Akkoma can be pretty strict 🤷🏻‍♀️

The version I was running when these requests happened, and still am running as I am writing this, is git commit ebfb617b2607970e58be934b8336dfc47be7414a probably better known as one commit after the v3.10.4 tag. (It was the head of `stable` when last I recompiled.) @snarfed, could it be some signature issue? That's another area where Akkoma can be pretty strict 🤷🏻‍♀️
Author

Hmm! Other fediverse servers have been accepting Bridgy Fed's signatures for a long time, but sure, it's definitely possible.

BF generates HTTP Sigs based on the cavage 12 draft standard. It includes the Date, Host, Content-Type, Digest, (SHA-256=...), and special (request-target) headers. Code: https://github.com/snarfed/bridgy-fed/blob/main/activitypub.py#L434-L487

Hmm! Other fediverse servers have been accepting Bridgy Fed's signatures for a long time, but sure, it's definitely possible. BF generates HTTP Sigs based on the cavage 12 draft standard. It includes the Date, Host, Content-Type, Digest, (`SHA-256=...`), and special `(request-target)` headers. Code: https://github.com/snarfed/bridgy-fed/blob/main/activitypub.py#L434-L487

I didn't dive too far into your implementation, but one thing that struck me in the comment was the (request-target) header

this is actually a pseudo-header that will not make it through most http clients/reverse proxies

I didn't dive too far into your implementation, but one thing that struck me in the comment was the (request-target) header this is actually a pseudo-header that will not make it through most http clients/reverse proxies
Contributor

Oh! And I am on a rev proxy!

Oh! And I am on a rev proxy!
Author

I didn't dive too far into your implementation, but one thing that struck me in the comment was the (request-target) header

this is actually a pseudo-header that will not make it through most http clients/reverse proxies

True! (request-target) isn't actually sent as an HTTP header, it's a special synthetic value that's only included in the HTTP Sig.

https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12#section-2.3
https://docs.joinmastodon.org/spec/security/#http

> I didn't dive too far into your implementation, but one thing that struck me in the comment was the (request-target) header > > this is actually a pseudo-header that will not make it through most http clients/reverse proxies True! `(request-target)` isn't actually sent as an HTTP header, it's a special synthetic value that's only included in the HTTP Sig. https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12#section-2.3 https://docs.joinmastodon.org/spec/security/#http
Author

I started compacting actor, author, and attributedTo down to just string ids in outgoing activities, and that got Akkoma to accept a Like! Replies are still 500ing though. Example delivered to https://akko.wtf/users/rei/inbox just now:

{
  "id": "https://fed.brid.gy/r/https://snarfed.org/2023-10-11_luna-nova-3#bridgy-fed-create",
  "actor": "https://fed.brid.gy/snarfed.org",
  "published": "2023-10-12T03:41:54.364982+00:00",
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Create",
  "object": {
    "published": "2023-10-11T20:41:49-07:00",
    "content": "<a class=\"u-in-reply-to\" href=\"https://akko.wtf/notice/ARP7mWTNZxbJAzPTIe\"></a>\n<div class=\"e-content\">\nsquawk\n</div>",
    "url": "https://fed.brid.gy/r/https://snarfed.org/2023-10-11_luna-nova-3",
    "id": "https://fed.brid.gy/r/https://snarfed.org/2023-10-11_luna-nova-3",
    "type": "Note",
    "attributedTo": "https://fed.brid.gy/snarfed.org",
    "inReplyTo": "https://akko.wtf/objects/8044263c-b1d3-495a-9a43-131bee571c80",
    "cc": [
      "https://akko.wtf/users/rei",
      "https://akko.wtf/users/rei/followers",
      "https://www.w3.org/ns/activitystreams#Public"
    ],
    "tag": [
      {
        "type": "Mention",
        "href": "https://akko.wtf/users/rei"
      },
      {
        "type": "Mention",
        "href": "https://akko.wtf/users/rei"
      }
    ],
    "to": [
      "https://www.w3.org/ns/activitystreams#Public"
    ]
  },
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ]
}
I started compacting `actor`, `author`, and `attributedTo` down to just string ids in outgoing activities, and that got Akkoma to accept a `Like`! Replies are still 500ing though. Example delivered to https://akko.wtf/users/rei/inbox just now: ```json { "id": "https://fed.brid.gy/r/https://snarfed.org/2023-10-11_luna-nova-3#bridgy-fed-create", "actor": "https://fed.brid.gy/snarfed.org", "published": "2023-10-12T03:41:54.364982+00:00", "@context": "https://www.w3.org/ns/activitystreams", "type": "Create", "object": { "published": "2023-10-11T20:41:49-07:00", "content": "<a class=\"u-in-reply-to\" href=\"https://akko.wtf/notice/ARP7mWTNZxbJAzPTIe\"></a>\n<div class=\"e-content\">\nsquawk\n</div>", "url": "https://fed.brid.gy/r/https://snarfed.org/2023-10-11_luna-nova-3", "id": "https://fed.brid.gy/r/https://snarfed.org/2023-10-11_luna-nova-3", "type": "Note", "attributedTo": "https://fed.brid.gy/snarfed.org", "inReplyTo": "https://akko.wtf/objects/8044263c-b1d3-495a-9a43-131bee571c80", "cc": [ "https://akko.wtf/users/rei", "https://akko.wtf/users/rei/followers", "https://www.w3.org/ns/activitystreams#Public" ], "tag": [ { "type": "Mention", "href": "https://akko.wtf/users/rei" }, { "type": "Mention", "href": "https://akko.wtf/users/rei" } ], "to": [ "https://www.w3.org/ns/activitystreams#Public" ] }, "to": [ "https://www.w3.org/ns/activitystreams#Public" ] } ```
Author

Hi again! Friendly ping, looks like this is still happening. Accepts of follows are affected too, along with replies and others mentioned above.

Hi again! Friendly ping, looks like this is still happening. `Accept`s of follows are affected too, along with replies and others mentioned above.
Contributor

@snarfed

Here are some (hopefully) relevant log messages from today:

May 09 13:14:08 halsen mix[1094]: 13:14:08.006 [notice] TLS :client: In state :hello received SERVER ALERT: Fatal - Internal Error
May 09 13:14:29 halsen mix[1094]: 13:14:29.455 request_id=F83NwrYlr4J9kVkABaTS [error] Follower/Following counter update for https://bsky.brid.gy/bsky.brid.gy failed.
May 09 13:14:29 halsen mix[1094]: {:error, :not_found}

and

May 09 13:26:28 halsen mix[1094]: 13:26:28.707 [warning] Description: 'Server authenticity is not verified since certificate path validation is not enabled'
May 09 13:26:28 halsen mix[1094]:      Reason: 'The option {verify, verify_peer} and one of the options \'cacertfile\' or \'cacerts\' are required to enable this.'
May 09 13:26:49 halsen mix[1094]: 13:26:49.512 request_id=F83ObwnEba4r6iIABmrR [error] Follower/Following counter update for https://bsky.brid.gy/bsky.brid.gy failed.
May 09 13:26:49 halsen mix[1094]: {:error, :not_found}
May 09 13:26:50 halsen mix[1094]: 13:26:50.380 request_id=F83Obz3ZUq4J4ngABbNi [error] Follower/Following counter update for https://bsky.brid.gy/bsky.brid.gy failed.
May 09 13:26:50 halsen mix[1094]: {:error, :not_found
@snarfed Here are some (hopefully) relevant log messages from today: May 09 13:14:08 halsen mix[1094]: 13:14:08.006 [notice] TLS :client: In state :hello received SERVER ALERT: Fatal - Internal Error May 09 13:14:29 halsen mix[1094]: 13:14:29.455 request_id=F83NwrYlr4J9kVkABaTS [error] Follower/Following counter update for https://bsky.brid.gy/bsky.brid.gy failed. May 09 13:14:29 halsen mix[1094]: {:error, :not_found} and May 09 13:26:28 halsen mix[1094]: 13:26:28.707 [warning] Description: 'Server authenticity is not verified since certificate path validation is not enabled' May 09 13:26:28 halsen mix[1094]: Reason: 'The option {verify, verify_peer} and one of the options \'cacertfile\' or \'cacerts\' are required to enable this.' May 09 13:26:49 halsen mix[1094]: 13:26:49.512 request_id=F83ObwnEba4r6iIABmrR [error] Follower/Following counter update for https://bsky.brid.gy/bsky.brid.gy failed. May 09 13:26:49 halsen mix[1094]: {:error, :not_found} May 09 13:26:50 halsen mix[1094]: 13:26:50.380 request_id=F83Obz3ZUq4J4ngABbNi [error] Follower/Following counter update for https://bsky.brid.gy/bsky.brid.gy failed. May 09 13:26:50 halsen mix[1094]: {:error, :not_found
Contributor

I am behind an nginx rev proxy 🤷🏻‍♀️

I am behind an nginx rev proxy 🤷🏻‍♀️

not sure why nginx factors into this, it's pretty clearly an issue validating outgoing requests, and is probably caused by your machine lacking ca-certificates or equivalent for your operating system

not sure why nginx factors into this, it's pretty clearly an issue validating outgoing requests, and is probably caused by your machine lacking ca-certificates or equivalent for your operating system

additionally those are just following counters, it's non-fatal

additionally those are just following counters, it's non-fatal
Contributor

The reason I mentioned I was behind a rev proxy was just in case it mattered since you earlier said that that might drop some pseudo headers.

We use let's encrypt 🤷🏻‍♀️

It's not me that said TLS :client: In state :hello received SERVER ALERT: Fatal - Internal Error, that came from the log.

The reason I mentioned I was behind a rev proxy was just in case it mattered since you earlier said that that might drop some pseudo headers. We use let's encrypt 🤷🏻‍♀️ It's not me that said `TLS :client: In state :hello received SERVER ALERT: Fatal - Internal Error`, that came from the log.

has it become less impossible to run a local instance of the software in question? last time this issue was raised I attempted to delve into it, only to be rebuffed by it more or less not being possible to test locally

if that hasn't changed, I'm not sure if we can do much about this, as the provided logs don't say much of note

has it become less impossible to run a local instance of the software in question? last time this issue was raised I attempted to delve into it, only to be rebuffed by it more or less not being possible to test locally if that hasn't changed, I'm not sure if we can do much about this, as the provided logs don't say much of note
Author

Sorry for the trouble! It's definitely possible to run locally, our docs (and CI) show how, but you're right that it's not easy to use locally to interact with other ActivityPub instances. It's not intended to be user-hosted, so we haven't prioritized that.

I'm happy to test against any Akkoma instance you want interactively though! And hopefully the example activities above help some. I can add more if you want.

Sorry for the trouble! It's definitely possible to run locally, [our docs](https://bridgy-fed.readthedocs.io/#development) ([and CI](https://github.com/snarfed/bridgy-fed/blob/main/.circleci%2Fconfig.yml)) show how, but you're right that it's not easy to use locally to interact with other ActivityPub instances. It's not intended to be user-hosted, so we haven't prioritized that. I'm happy to test against any Akkoma instance you want interactively though! And hopefully the example activities above help some. I can add more if you want.
Member

The object part of the old reply sample no longer matches what’s served when fetching atm and fetching the create activity is impossible. Could you share an updated sample?

And btw Create activities not being fetchable, to my understanding violates AP spec; except for anonymous (nullid) and transient objects (no id) must be retrievable via their id (given sufficient perms). See https://www.w3.org/TR/activitypub/#obj-id and the following section

The object part of the old reply sample no longer matches what’s served when fetching atm and fetching the create activity is impossible. Could you share an updated sample? And btw Create activities not being fetchable, to my understanding violates AP spec; except for anonymous (`null`id) and transient objects (no id) must be retrievable via their id (given sufficient perms). See https://www.w3.org/TR/activitypub/#obj-id and the following section
Author

Sure! Here's a reply I delivered to https://ihatebeinga.live/users/FloatingGhost/inbox just now. That POST returned
401 Request not signed (not 500), even though the request did have a valid HTTP Sig with keyID https://fed.brid.gy/snarfed.org#key , and other fediverse servers are happily accepting these sigs and activities.

Is this problem maybe that https://w3id.org/security/v1 isn't in @context? JSON-LD processing is supposed to be optional in AP, but I should probably still add that.

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Create",
  "id": "https://fed.brid.gy/r/https://snarfed.org/2024-05-10_sprout-the-strongest-bocchi-evil-you-can-only-pick-one-form-of-mastodon#bridgy-fed-create",
  "actor": "https://fed.brid.gy/snarfed.org",
  "published": "2024-05-10T17:45:14.396845+00:00",
  "object": {
    "type": "Note",
    "id": "https://fed.brid.gy/r/https://snarfed.org/2024-05-10_sprout-the-strongest-bocchi-evil-you-can-only-pick-one-form-of-mastodon",
    "published": "2024-05-10T10:45:08-07:00",
    "content": "<a class=\"u-in-reply-to\" href=\"https://mastodon.social/@FloatingGhost@ihatebeinga.live/112416247639494094\"></a>\n<div class=\"e-content\">\n😢\n</div>",
    "url": "https://fed.brid.gy/r/https://snarfed.org/2024-05-10_sprout-the-strongest-bocchi-evil-you-can-only-pick-one-form-of-mastodon",
    "attributedTo": "https://fed.brid.gy/snarfed.org",
    "inReplyTo": "https://ihatebeinga.live/objects/60d87dc9-7740-4f66-8f6a-f811918ac724",
    "contentMap": { "en": "<a class=\"u-in-reply-to\" href=\"https://mastodon.social/@FloatingGhost@ihatebeinga.live/112416247639494094\"></a>\n<div class=\"e-content\">\n😢\n</div>" },
    "tag": [{
        "type": "Mention",
        "href": "https://ihatebeinga.live/users/FloatingGhost"
      }],
    "to": ["https://www.w3.org/ns/activitystreams#Public"],
    "cc": [
      "https://ihatebeinga.live/users/FloatingGhost",
      "https://www.w3.org/ns/activitystreams#Public",
      "https://ihatebeinga.live/users/FloatingGhost/followers"
    ]
  },
  "to": ["https://www.w3.org/ns/activitystreams#Public"]
}

(Re fetching Create activities, that would definitely be nice! It's not entirely clear whether the "MUST present" in https://www.w3.org/TR/activitypub/#retrieving-objects is a MUST for serving in general, or only for handling conneg if you serve...but regardless, ideally yes they should be fetchable. The catch here is that fetching ids with fragments is a famously undefined open question, https://github.com/w3c/activitypub/issues/367 , and Bridgy Fed's Create ids only differ from the enclosed object's id by a fragment. We're not alone in using fragments in ids like that, but we're obviously not helping either. Sorry.)

Sure! Here's a reply I delivered to https://ihatebeinga.live/users/FloatingGhost/inbox just now. That POST returned 401 `Request not signed` (not 500), even though the request did have a valid HTTP Sig with `keyID` https://fed.brid.gy/snarfed.org#key , and other fediverse servers are happily accepting these sigs and activities. Is this problem maybe that `https://w3id.org/security/v1` isn't in `@context`? JSON-LD processing is supposed to be optional in AP, but I should probably still add that. ```json { "@context": "https://www.w3.org/ns/activitystreams", "type": "Create", "id": "https://fed.brid.gy/r/https://snarfed.org/2024-05-10_sprout-the-strongest-bocchi-evil-you-can-only-pick-one-form-of-mastodon#bridgy-fed-create", "actor": "https://fed.brid.gy/snarfed.org", "published": "2024-05-10T17:45:14.396845+00:00", "object": { "type": "Note", "id": "https://fed.brid.gy/r/https://snarfed.org/2024-05-10_sprout-the-strongest-bocchi-evil-you-can-only-pick-one-form-of-mastodon", "published": "2024-05-10T10:45:08-07:00", "content": "<a class=\"u-in-reply-to\" href=\"https://mastodon.social/@FloatingGhost@ihatebeinga.live/112416247639494094\"></a>\n<div class=\"e-content\">\n😢\n</div>", "url": "https://fed.brid.gy/r/https://snarfed.org/2024-05-10_sprout-the-strongest-bocchi-evil-you-can-only-pick-one-form-of-mastodon", "attributedTo": "https://fed.brid.gy/snarfed.org", "inReplyTo": "https://ihatebeinga.live/objects/60d87dc9-7740-4f66-8f6a-f811918ac724", "contentMap": { "en": "<a class=\"u-in-reply-to\" href=\"https://mastodon.social/@FloatingGhost@ihatebeinga.live/112416247639494094\"></a>\n<div class=\"e-content\">\n😢\n</div>" }, "tag": [{ "type": "Mention", "href": "https://ihatebeinga.live/users/FloatingGhost" }], "to": ["https://www.w3.org/ns/activitystreams#Public"], "cc": [ "https://ihatebeinga.live/users/FloatingGhost", "https://www.w3.org/ns/activitystreams#Public", "https://ihatebeinga.live/users/FloatingGhost/followers" ] }, "to": ["https://www.w3.org/ns/activitystreams#Public"] } ``` (Re fetching `Create` activities, that would definitely be nice! It's not entirely clear whether the "_MUST_ present" in https://www.w3.org/TR/activitypub/#retrieving-objects is a MUST for serving in general, or only for handling conneg _if_ you serve...but regardless, ideally yes they should be fetchable. The catch here is that fetching ids with fragments is a famously undefined open question, https://github.com/w3c/activitypub/issues/367 , and Bridgy Fed's `Create` ids only differ from the enclosed object's id by a fragment. We're not alone in using fragments in ids like that, but we're obviously not helping either. Sorry.)

keyID https://fed.brid.gy/snarfed.org#key

there's your issue

fixed via 4457928e32

> keyID https://fed.brid.gy/snarfed.org#key there's your issue fixed via 4457928e325cf370a0d2e028232dbd0a542547e0
Member

keyID https://fed.brid.gy/snarfed.org#key

there's your issue

fixed via 4457928e32

i don’t think this is it

Fragments and query parameters were already deleted in key_id_to_actor; known_suffixes only deals with actual different paths. In an ideal setting actor <-> key id would be a well specified conversion, but afaik it’s not and thus we ideally shouldn't rely on being able to transform this at all though, but look up the actor and check if the key id matches (but this requires DB lookups and is thus more costly)

> > keyID https://fed.brid.gy/snarfed.org#key > > there's your issue > > fixed via 4457928e32 i don’t think this is it Fragments and query parameters were already deleted in `key_id_to_actor`; `known_suffixes` only deals with actual different paths. In an ideal setting actor <-> key id would be a well specified conversion, but afaik it’s not and thus we ideally shouldn't rely on being able to transform this at all though, but look up the actor and check if the key id matches (but this requires DB lookups and is thus more costly)

if that doesn't fix it, then eh

with the utter lack of reproducibility and the only debugging we have being very slow live systems interactions, I will leave this for the bridgy people to solve

if that doesn't fix it, then eh with the utter lack of reproducibility and the only debugging we have being very slow live systems interactions, I will leave this for the bridgy people to solve
Author
Sadly true. https://swicg.github.io/activitypub-http-signature/#how-to-obtain-a-signature-s-public-key
Author

Fair enough!

Fair enough!
Member

The object part of the old reply sample no longer matches what’s served when fetching atm and fetching the create activity is impossible. Could you share an updated sample?

Sure! Here's a reply I delivered to https://ihatebeinga.live/users/FloatingGhost/inbox just now

ok, so i can again confirm that the current full Activity passes successfully through our pipeline once it reaches Federator.incoming_ap_doc. Meaning on the upside things should just work™ once the signing issue is figured out, no additional lurking problems.

Some comments on the activity though:

  • fetching the object gives a different response than what’s actively federated to other instances; e.g. cc and tag are missing. This is not breaking parsing the document but means servers later fetching the object as part of thread expansion will see it differently than those receiving it through federation
  • both to and cc contains the public address; Akkoma treats this as a public post but it is conceivable this may confuse some implementations into thinking it’s unlisted/quiet-public/home whatever you want to call it
  • cc contains the follower collection address of the person your sending to; i don’t think you should do this (followers of person A want to get posts from A, not from whoever decides to use A’s following address) and Akkoma strips this out

Debugging the signing issue without being able to locally test things is hard, but here are some pointers which hopefully help you or whoever decides to pick this up, to determine the issue:

      {:http_signatures,
       git: "https://akkoma.dev/AkkomaGang/http_signatures.git",
       ref: "6640ce7d24c783ac2ef56e27d00d12e8dc85f396"},

with e.g.:

      {:http_signatures, path: "../httpsig_for_debugging"}

And also updated links for Bridgy’s signing code:

Compliments for actually resigning on redirects btw, most fedi software doesn't (necessitating workarounds to not break federation) albeit it’s definitely the right thing to do ;^^
On cursory loook though (i might easily miss something here, sorry if that’s the case), doesn’t from_user get lost on redirects and there’s no protection against redirect loops stalling everything?

> > The object part of the old reply sample no longer matches what’s served when fetching atm and fetching the create activity is impossible. Could you share an updated sample? > > Sure! Here's a reply I delivered to https://ihatebeinga.live/users/FloatingGhost/inbox just now ok, so i can again confirm that the current full Activity passes successfully through our pipeline once it reaches `Federator.incoming_ap_doc`. Meaning on the upside things should just work™ once the signing issue is figured out, no additional lurking problems. Some comments on the activity though: - fetching the object gives a different response than what’s actively federated to other instances; e.g. `cc` and `tag` are missing. This is not breaking parsing the document but means servers later fetching the object as part of thread expansion will see it differently than those receiving it through federation - both `to` and `cc` contains the public address; Akkoma treats this as a public post but it is conceivable this may confuse some implementations into thinking it’s unlisted/quiet-public/home whatever you want to call it - `cc` contains the follower collection address of the person your sending to; i don’t think you should do this (followers of person A want to get posts from A, not from whoever decides to use A’s following address) and Akkoma strips this out --- Debugging the signing issue without being able to locally test things is hard, but here are some pointers which hopefully help you or whoever decides to pick this up, to determine the issue: - Guide for [setting up local Akkoma dev instance](https://akkoma.dev/AkkomaGang/akkoma/src/branch/develop/docs/docs/development/setting_up_akkoma_dev.md) - Our HTTP signing library: https://akkoma.dev/AkkomaGang/http_signatures - And the accompanying adapter to get key<->actor mappings: https://akkoma.dev/AkkomaGang/akkoma/src/branch/develop/lib/pleroma/signature.ex - to insert debug prints in Elixir use `IO.puts("text text #{inspect(variable3)} more text")` - to use a modified version of the HTTPSignature lib replace this part in `mix.exs` ```elixir {:http_signatures, git: "https://akkoma.dev/AkkomaGang/http_signatures.git", ref: "6640ce7d24c783ac2ef56e27d00d12e8dc85f396"}, ``` with e.g.: ```elixir {:http_signatures, path: "../httpsig_for_debugging"} ``` And also updated links for Bridgy’s signing code: - https://github.com/snarfed/bridgy-fed/blob/2b449c6d3191d8a435444b6788f098212214127b/activitypub.py#L530 - https://github.com/snarfed/httpsig/blob/master/httpsig/requests_auth.py Compliments for actually resigning on redirects btw, most fedi software doesn't (necessitating workarounds to not break federation) albeit it’s definitely the right thing to do ;^^ On cursory loook though (i might easily miss something here, sorry if that’s the case), doesn’t `from_user` get lost on redirects and there’s no protection against redirect loops stalling everything?
Contributor

cc contains the follower collection address of the person your sending to; i don’t think you should do this (followers of person A want to get posts from A, not from whoever decides to use A’s following address) and Akkoma strips this out

For a reply, this is correct imo. It's true that most micro blogging software on fedi don't do this, but the spec actually asks you to address the followers collection of who you reply to[1], so bridgy is the one doing it correctly.

If people on the target server don't want to see these, then it's up to that server to handle that[2]. (Which Akkoma apparently does by stripping it out, so OK.)


See https://www.w3.org/TR/activitypub/#inbox-forwarding

[1]

The following section is to mitigate the "ghost replies" problem which occasionally causes problems on federated networks. This problem is best demonstrated with an example.

Alyssa makes a post about her having successfully presented a paper at a conference and sends it to her followers collection, which includes her friend Ben. Ben replies to Alyssa's message congratulating her and includes her followers collection on the recipients. However, Ben has no access to see the members of Alyssa's followers collection, so his server does not forward his messages to their inbox. Without the following mechanism, if Alyssa were then to reply to Ben, her followers would see Alyssa replying to Ben without having ever seen Ben interacting. This would be very confusing!

[2]

The server MAY filter its delivery targets according to implementation-specific rules (for example, spam filtering).

> cc contains the follower collection address of the person your sending to; i don’t think you should do this (followers of person A want to get posts from A, not from whoever decides to use A’s following address) and Akkoma strips this out For a reply, this is correct imo. It's true that most micro blogging software on fedi don't do this, but the spec actually asks you to address the followers collection of who you reply to[1], so bridgy is the one doing it correctly. If people on the target server don't want to see these, then it's up to that server to handle that[2]. (Which Akkoma apparently does by stripping it out, so OK.) *** See https://www.w3.org/TR/activitypub/#inbox-forwarding [1] > The following section is to mitigate the "ghost replies" problem which occasionally causes problems on federated networks. This problem is best demonstrated with an example. > > Alyssa makes a post about her having successfully presented a paper at a conference and sends it to her followers collection, which includes her friend Ben. Ben replies to Alyssa's message congratulating her and includes her followers collection on the recipients. However, Ben has no access to see the members of Alyssa's followers collection, so his server does not forward his messages to their inbox. Without the following mechanism, if Alyssa were then to reply to Ben, her followers would see Alyssa replying to Ben without having ever seen Ben interacting. This would be very confusing! [2] > The server MAY filter its delivery targets according to implementation-specific rules (for example, spam filtering).
Member

but the spec actually asks you to address the followers collection of who you reply to[1], so bridgy is the one doing it correctly.

Thanks for clarifying, TIL!

One more remark i forgot to add before:

Is this problem maybe that https://w3id.org/security/v1 isn't in @context? JSON-LD processing is supposed to be optional in AP, but I should probably still add that.

The ActivityStreams profile of JSON-LD restricts it such that no JSON-LD processing is required to parse a document, but it’s still a JSON-LD document and yes not specifying all used contexts can lead to interop issues with implementations which do employ JSON-LD processing (not Akkoma but see e.g. #717). The ActivityStream 2.0 spec also mentions this:

The serialized JSON form of an Activity Streams 2.0 document MUST be consistent with what would be produced by the standard JSON-LD 1.0 Processing Algorithms and API [JSON-LD-API] Compaction Algorithm using, at least, the normative JSON-LD @context definition provided here. Implementations MAY augment the provided @context with additional @context definitions but MUST NOT override or change the normative context. Implementations MAY also use additional properties and values not defined in the JSON-LD @context with the understanding that any such properties will likely be unsupported and ignored by consuming implementations that use the standard JSON-LD algorithms. See the Extensibility section for more information on handling extensions within Activity Streams 2.0 documents.

> but the spec actually asks you to address the followers collection of who you reply to[1], so bridgy is the one doing it correctly. Thanks for clarifying, TIL! One more remark i forgot to add before: > Is this problem maybe that https://w3id.org/security/v1 isn't in @context? JSON-LD processing is supposed to be optional in AP, but I should probably still add that. The ActivityStreams profile of JSON-LD restricts it such that no JSON-LD processing is required to parse a document, but it’s still a JSON-LD document and yes not specifying all used contexts can lead to interop issues with implementations which do employ JSON-LD processing (not Akkoma but see e.g. #717). The [ActivityStream 2.0 spec](https://www.w3.org/TR/activitystreams-core/#jsonld) also mentions this: > The serialized JSON form of an Activity Streams 2.0 document MUST be consistent with what would be produced by the standard JSON-LD 1.0 Processing Algorithms and API [JSON-LD-API] Compaction Algorithm using, at least, the normative JSON-LD @context definition provided here. Implementations MAY augment the provided @context with additional @context definitions but MUST NOT override or change the normative context. Implementations MAY also use additional properties and values not defined in the JSON-LD @context with the understanding that any such properties will likely be unsupported and ignored by consuming implementations that use the standard JSON-LD algorithms. See the Extensibility section for more information on handling extensions within Activity Streams 2.0 documents.
Author

ok, so i can again confirm that the current full Activity passes successfully through our pipeline once it reaches Federator.incoming_ap_doc. Meaning on the upside things should just work™ once the signing issue is figured out, no additional lurking problems.

@Oneric thank you for confirming that! And for all the info and sleuthing, I really appreciate it. Sounds like https://w3id.org/security/v1 missing from @context is our clearest lead so far, I'll add that and try again and keep you all posted.

fetching the object gives a different response than what’s actively federated to other instances; e.g. cc and tag are missing. This is not breaking parsing the document but means servers later fetching the object as part of thread expansion will see it differently than those receiving it through federation

Huh, this is news to me, I'll definitely look.

On cursory loook though (i might easily miss something here, sorry if that’s the case), doesn’t from_user get lost on redirects and there’s no protection against redirect loops stalling everything?

Good questions! Our GETs should all be signed by the instance actor, and POSTs shouldn't follow redirects, so in practice this shouldn't matter, but it'd be good hygiene to still pass from_user, agreed. I'll do that.

And you're right, I don't explicitly protect against redirect loops, I let it fall through to the overall request handling deadline. Not a high priority in my threat model, but couldn't hurt to add. Thanks!

> ok, so i can again confirm that the current full Activity passes successfully through our pipeline once it reaches Federator.incoming_ap_doc. Meaning on the upside things should just work™ once the signing issue is figured out, no additional lurking problems. @Oneric thank you for confirming that! And for all the info and sleuthing, I really appreciate it. Sounds like `https://w3id.org/security/v1` missing from `@context` is our clearest lead so far, I'll add that and try again and keep you all posted. > fetching the object gives a different response than what’s actively federated to other instances; e.g. cc and tag are missing. This is not breaking parsing the document but means servers later fetching the object as part of thread expansion will see it differently than those receiving it through federation Huh, this is news to me, I'll definitely look. > On cursory loook though (i might easily miss something here, sorry if that’s the case), doesn’t from_user get lost on redirects and there’s no protection against redirect loops stalling everything? Good questions! Our GETs should all be signed by the instance actor, and POSTs shouldn't follow redirects, so in practice this shouldn't matter, but it'd be good hygiene to still pass `from_user`, agreed. I'll do that. And you're right, I don't explicitly protect against redirect loops, I let it fall through to the overall request handling deadline. Not a high priority in my threat model, but couldn't hurt to add. Thanks!
Author

Actually, now that I think about it, our outbound activities like #438 (comment) don't include https://w3id.org/security/v1 in @context because they don't use anything from that context. Only actors do, in publicKey etc, and Bridgy Fed's actors do include it, eg https://fed.brid.gy/snarfed.org .

I'm still happy to add it if it'll fix things on the Akkoma side, but now I suspect that it won't...

Actually, now that I think about it, our outbound activities like https://akkoma.dev/AkkomaGang/akkoma/issues/438#issuecomment-12010 don't include `https://w3id.org/security/v1` in `@context` because they don't use anything from that context. Only actors do, in `publicKey` etc, and Bridgy Fed's actors do include it, eg https://fed.brid.gy/snarfed.org . I'm still happy to add it if it'll fix things on the Akkoma side, but now I suspect that it won't...
Member

it won’t fix federation with Akkoma, since Akkoma doesn’t do JSON-LD processing; including it for actors might fix federation with some other servers though (similarly for other Mastodon etc extensions)

it won’t fix federation with Akkoma, since Akkoma doesn’t do JSON-LD processing; including it for actors might fix federation with some other servers though (similarly for other Mastodon etc extensions)
Author

Yup, thanks. Our actors do include it. So, the sig incompatibility needs more investigation. OK! Thanks again for the links and info. Not my top priority right now, but I'll circle back and dig into this again sooner or later. 4457928e32 sounded likely, but I know you said that wasn't it. I'll also re-plug https://swicg.github.io/activitypub-http-signature/#how-to-obtain-a-signature-s-public-key as maybe the closest thing we have to an authoritative process for fetching a key and verifying a signature in the fediverse. 🤷

Yup, thanks. Our actors do include it. So, the sig incompatibility needs more investigation. OK! Thanks again for the links and info. Not my top priority right now, but I'll circle back and dig into this again sooner or later. 4457928e32 sounded likely, but I know you said that wasn't it. I'll also re-plug https://swicg.github.io/activitypub-http-signature/#how-to-obtain-a-signature-s-public-key as maybe the closest thing we have to an authoritative process for fetching a key and verifying a signature in the fediverse. 🤷
Member

it seems unlikey the problem is matching key to actor, else (in recent'ish) akkoma errors would already be logged when fetching the actor, but i didn’t see any such errors

it seems unlikey the problem is matching key to actor, else (in recent'ish) akkoma errors would already be logged when fetching the actor, but i didn’t see any such errors
Member

@snarfed if you could provide a full request, including all headers exactly as sent it might also be possible to tell more or maybe even reproduce this without a real local bridgy instance.

If it’s not easily possible to directly grab outgoing requests from Bridgy itself, temporary setting up an instance of ilja’s ap_logger may be helpful

@snarfed if you could provide a full request, including all headers exactly as sent it might also be possible to tell more or maybe even reproduce this without a real local bridgy instance. If it’s not easily possible to directly grab outgoing requests from Bridgy itself, temporary setting up an instance of ilja’s [ap_logger](https://codeberg.org/ilja/ap_logger) may be helpful
Author

@Oneric sure, thank you for the offer! Here's a like I sent for https://akko.wtf/notice/AjAjPS5ikUpMpWGqP2 just now:

POST /users/Oneric/inbox HTTP/1.1

user-agent: Bridgy Fed (https://fed.brid.gy/)
accept-encoding: gzip, deflate
accept: */*
connection: keep-alive
date: Mon, 15 Jul 2024 04:35:16 GMT
host: akko.wtf
content-type: application/ld+json; profile="https://www.w3.org/ns/activitystreams"
digest: SHA-256=aD6VWn+4CUs5hBeEjAiSdRjga/h5Qnub0gLVSqSosSk=
Content-Length: 701
signature: keyId="https://fed.brid.gy/snarfed.org#key",algorithm="rsa-sha256",signature="pezDShRpsN8sErE5xOPWh4iJOzi2nz7IcwxtCYEQ3KfqezkQIGFb9lxCwgUZDUdueX6R/eUXGJ5S5GdzjEMbs90iIzaqvN2fRmhs3VHgd7Y1O8bMJGfkOz5i6JllyPMgfFT9937147ATmGTmo7h4NhE5XgQ6VwNijNuBhLGOZsCLYqldpuyGuHXyLOmGHsIL/tBf0LBKPMyE6S70hEmh6Zx7nQeW5Kt9CTZIxnSbeFrg2HMiMaFRM5gWtPTH5N6Jr6SGal/vXT9VrN1wjwZI83y45aHmpnU7VImSWGWyXxcveHvspXtDciWAPNU8ng6aglYVgWrLaQz70ApRb10fXg==",headers="date host digest (request-target)"

{"published":"2024-07-14T21:35:10-07:00","content":"<p><div class=\\"e-content\\">Luna Nova</div></p>","url":"https://fed.brid.gy/r/https://snarfed.org/2024-07-14_luna-nova-5","actor":"https://fed.brid.gy/snarfed.org","id":"https://fed.brid.gy/r/https://snarfed.org/2024-07-14_luna-nova-5","@context":"https://www.w3.org/ns/activitystreams","type":"Like","object":"https://akko.wtf/objects/e60cda8e-17fb-4b99-b732-fb8a6553d7b2","cc":["https://akko.wtf/users/Oneric","https://www.w3.org/ns/activitystreams#Public","https://akko.wtf/users/Oneric/followers"],"to":["https://www.w3.org/ns/activitystreams#Public"],"content_is_html":true,"contentMap":{"en":"<p><div class=\\"e-content\\">Luna Nova</div></p>"}}
@Oneric sure, thank you for the offer! Here's a like I sent for https://akko.wtf/notice/AjAjPS5ikUpMpWGqP2 just now: ``` POST /users/Oneric/inbox HTTP/1.1 user-agent: Bridgy Fed (https://fed.brid.gy/) accept-encoding: gzip, deflate accept: */* connection: keep-alive date: Mon, 15 Jul 2024 04:35:16 GMT host: akko.wtf content-type: application/ld+json; profile="https://www.w3.org/ns/activitystreams" digest: SHA-256=aD6VWn+4CUs5hBeEjAiSdRjga/h5Qnub0gLVSqSosSk= Content-Length: 701 signature: keyId="https://fed.brid.gy/snarfed.org#key",algorithm="rsa-sha256",signature="pezDShRpsN8sErE5xOPWh4iJOzi2nz7IcwxtCYEQ3KfqezkQIGFb9lxCwgUZDUdueX6R/eUXGJ5S5GdzjEMbs90iIzaqvN2fRmhs3VHgd7Y1O8bMJGfkOz5i6JllyPMgfFT9937147ATmGTmo7h4NhE5XgQ6VwNijNuBhLGOZsCLYqldpuyGuHXyLOmGHsIL/tBf0LBKPMyE6S70hEmh6Zx7nQeW5Kt9CTZIxnSbeFrg2HMiMaFRM5gWtPTH5N6Jr6SGal/vXT9VrN1wjwZI83y45aHmpnU7VImSWGWyXxcveHvspXtDciWAPNU8ng6aglYVgWrLaQz70ApRb10fXg==",headers="date host digest (request-target)" {"published":"2024-07-14T21:35:10-07:00","content":"<p><div class=\\"e-content\\">Luna Nova</div></p>","url":"https://fed.brid.gy/r/https://snarfed.org/2024-07-14_luna-nova-5","actor":"https://fed.brid.gy/snarfed.org","id":"https://fed.brid.gy/r/https://snarfed.org/2024-07-14_luna-nova-5","@context":"https://www.w3.org/ns/activitystreams","type":"Like","object":"https://akko.wtf/objects/e60cda8e-17fb-4b99-b732-fb8a6553d7b2","cc":["https://akko.wtf/users/Oneric","https://www.w3.org/ns/activitystreams#Public","https://akko.wtf/users/Oneric/followers"],"to":["https://www.w3.org/ns/activitystreams#Public"],"content_is_html":true,"contentMap":{"en":"<p><div class=\\"e-content\\">Luna Nova</div></p>"}} ```
Member

huh, turning this into a curl request for my local dev instance running current develop (3ff0f46b9f) and adding some logging print shows... the signature is successfully verified?
It then still errors later because the localhost user doesn’t show up in recipients (altering the body predictable leads to sig failures due to digest change), but iirc we tested injecting some Creates before and even if, such later failures shouldn’t yield a signature error response...

Note: i manually fetched the https://fed.brid.gy/snarfed.org actor before, but since brid.gy doesn’t appear to require auth fetches for actors, this doesn’t seem like a bootstrapping issue either

MIX_ENV=prod or the default dev env doesn’t appear to make a difference

The tested versions should match what’s deployed on akko.wtf apart from some doc changes

Are you sure this really exactly matches what other servers receive; Does some intermediate framework or proxy maybe alter something?

curl request for local dev instance
#!/bin/sh
curl -X POST -i \
  -H 'user-agent: Bridgy Fed (https://fed.brid.gy/)' \
  -H 'accept-encoding: gzip, deflate' \
  -H 'accept: */*' \
  -H 'connection: keep-alive' \
  -H 'date: Mon, 15 Jul 2024 04:35:16 GMT' \
  -H 'host: akko.wtf' \
  -H 'content-type: application/ld+json; profile="https://www.w3.org/ns/activitystreams"' \
  -H 'digest: SHA-256=aD6VWn+4CUs5hBeEjAiSdRjga/h5Qnub0gLVSqSosSk=' \
  -H 'Content-Length: 701' \
  -H 'signature: keyId="https://fed.brid.gy/snarfed.org#key",algorithm="rsa-sha256",signature="pezDShRpsN8sErE5xOPWh4iJOzi2nz7IcwxtCYEQ3KfqezkQIGFb9lxCwgUZDUdueX6R/eUXGJ5S5GdzjEMbs90iIzaqvN2fRmhs3VHgd7Y1O8bMJGfkOz5i6JllyPMgfFT9937147ATmGTmo7h4NhE5XgQ6VwNijNuBhLGOZsCLYqldpuyGuHXyLOmGHsIL/tBf0LBKPMyE6S70hEmh6Zx7nQeW5Kt9CTZIxnSbeFrg2HMiMaFRM5gWtPTH5N6Jr6SGal/vXT9VrN1wjwZI83y45aHmpnU7VImSWGWyXxcveHvspXtDciWAPNU8ng6aglYVgWrLaQz70ApRb10fXg==",headers="date host digest (request-target)"' \
  --data \
'{"published":"2024-07-14T21:35:10-07:00","content":"<p><div class=\"e-content\">Luna Nova</div></p>","url":"https://fed.brid.gy/r/https://snarfed.org/2024-07-14_luna-nova-5","actor":"https://fed.brid.gy/snarfed.org","id":"https://fed.brid.gy/r/https://snarfed.org/2024-07-14_luna-nova-5","@context":"https://www.w3.org/ns/activitystreams","type":"Like","object":"https://akko.wtf/objects/e60cda8e-17fb-4b99-b732-fb8a6553d7b2","cc":["https://akko.wtf/users/Oneric","https://www.w3.org/ns/activitystreams#Public","https://akko.wtf/users/Oneric/followers"],"to":["https://www.w3.org/ns/activitystreams#Public"],"content_is_html":true,"contentMap":{"en":"<p><div class=\"e-content\">Luna Nova</div></p>"}}' \
  http://localhost:4000/users/Oneric/inbox
Extra logging diff
diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
index 6642f7771..7fa69e8e4 100644
--- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
@@ -272,27 +272,35 @@ def outbox(conn, %{"nickname" => nickname}) do
   end
 
   def inbox(%{assigns: %{valid_signature: true}} = conn, %{"nickname" => nickname} = params) do
+    IO.puts("POST to user inbox with valid sig:\n#{inspect(conn.req_headers)}\n\n#{inspect(params)}\n")
     with %User{} = recipient <- User.get_cached_by_nickname(nickname),
          {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(params["actor"]),
          true <- Utils.recipient_in_message(recipient, actor, params),
          params <- Utils.maybe_splice_recipient(recipient.ap_id, params) do
       Federator.incoming_ap_doc(params)
       json(conn, "ok")
+    else
+      e ->
+        IO.puts("ooopsie: #{inspect(e)}")
+        {:error, :internal}
     end
   end
 
   def inbox(%{assigns: %{valid_signature: true}} = conn, params) do
+    IO.puts("POST to server inbox with valid sig:\n#{inspect(conn.req_headers)}\n\n#{inspect(params)}\n")
     Federator.incoming_ap_doc(params)
     json(conn, "ok")
   end
 
-  def inbox(%{assigns: %{valid_signature: false}} = conn, _params) do
+  def inbox(%{assigns: %{valid_signature: false}} = conn, params) do
+    IO.puts("POST to an inbox with invalid sig:\n#{inspect(conn.req_headers)}\n\n#{inspect(params)}\n")
     conn
     |> put_status(:bad_request)
     |> json("Invalid HTTP Signature")
   end
 
-  def inbox(conn, _params) do
+  def inbox(conn, params) do
+    IO.puts("POST to an inbox _without_ any sig:\n#{inspect(conn.req_headers)}\n\n#{inspect(params)}\n")
     conn
     |> put_status(:bad_request)
     |> json("error, missing HTTP Signature")
Debug log output
POST to user inbox with valid sig:
[{"accept", "*/*"}, {"accept-encoding", "gzip, deflate"}, {"connection", "keep-alive"}, {"content-length", "701"}, {"content-type", "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""}, {"date", "Mon, 15 Jul 2024 04:35:16 GMT"}, {"digest", "SHA-256=aD6VWn+4CUs5hBeEjAiSdRjga/h5Qnub0gLVSqSosSk="}, {"host", "akko.wtf"}, {"signature", "keyId=\"https://fed.brid.gy/snarfed.org#key\",algorithm=\"rsa-sha256\",signature=\"pezDShRpsN8sErE5xOPWh4iJOzi2nz7IcwxtCYEQ3KfqezkQIGFb9lxCwgUZDUdueX6R/eUXGJ5S5GdzjEMbs90iIzaqvN2fRmhs3VHgd7Y1O8bMJGfkOz5i6JllyPMgfFT9937147ATmGTmo7h4NhE5XgQ6VwNijNuBhLGOZsCLYqldpuyGuHXyLOmGHsIL/tBf0LBKPMyE6S70hEmh6Zx7nQeW5Kt9CTZIxnSbeFrg2HMiMaFRM5gWtPTH5N6Jr6SGal/vXT9VrN1wjwZI83y45aHmpnU7VImSWGWyXxcveHvspXtDciWAPNU8ng6aglYVgWrLaQz70ApRb10fXg==\",headers=\"date host digest (request-target)\""}, {"user-agent", "Bridgy Fed (https://fed.brid.gy/)"}, {"(request-target)", "post /users/Oneric/inbox"}]

%{"@context" => "https://www.w3.org/ns/activitystreams", "actor" => "https://fed.brid.gy/snarfed.org", "cc" => ["https://akko.wtf/users/Oneric", "https://www.w3.org/ns/activitystreams#Public", "https://akko.wtf/users/Oneric/followers"], "content" => "<p><div class=\"e-content\">Luna Nova</div></p>", "contentMap" => %{"en" => "<p><div class=\"e-content\">Luna Nova</div></p>"}, "content_is_html" => true, "id" => "https://fed.brid.gy/r/https://snarfed.org/2024-07-14_luna-nova-5", "nickname" => "Oneric", "object" => "https://akko.wtf/objects/e60cda8e-17fb-4b99-b732-fb8a6553d7b2", "published" => "2024-07-14T21:35:10-07:00", "to" => ["https://www.w3.org/ns/activitystreams#Public"], "type" => "Like", "url" => "https://fed.brid.gy/r/https://snarfed.org/2024-07-14_luna-nova-5"}

ooopsie: false

EDIT: Perhaps worth noting i replaced some double backslashes \\ in the originally posted request with a single backslash \ each, else it wasn’t valid JSON (and gave a different error). I’m assuming this is just an artefact of whatever this was copied from

huh, turning this into a curl request for my local dev instance running current develop (3ff0f46b9f36cb6ccdc95ce78e0603de18b193ab) and adding some logging print shows... the signature is successfully verified? It then still errors later because the localhost user doesn’t show up in recipients (altering the body predictable leads to sig failures due to digest change), but iirc we tested injecting some `Create`s before and even if, such later failures shouldn’t yield a signature error response... Note: i manually fetched the `https://fed.brid.gy/snarfed.org` actor before, but since brid.gy doesn’t appear to require auth fetches for actors, this doesn’t seem like a bootstrapping issue either `MIX_ENV=prod` or the default `dev` env doesn’t appear to make a difference The tested versions should match what’s deployed on akko.wtf apart from some doc changes Are you sure this really exactly matches what other servers receive; Does some intermediate framework or proxy maybe alter something? <details> <summary>curl request for local dev instance</summary> ```sh #!/bin/sh curl -X POST -i \ -H 'user-agent: Bridgy Fed (https://fed.brid.gy/)' \ -H 'accept-encoding: gzip, deflate' \ -H 'accept: */*' \ -H 'connection: keep-alive' \ -H 'date: Mon, 15 Jul 2024 04:35:16 GMT' \ -H 'host: akko.wtf' \ -H 'content-type: application/ld+json; profile="https://www.w3.org/ns/activitystreams"' \ -H 'digest: SHA-256=aD6VWn+4CUs5hBeEjAiSdRjga/h5Qnub0gLVSqSosSk=' \ -H 'Content-Length: 701' \ -H 'signature: keyId="https://fed.brid.gy/snarfed.org#key",algorithm="rsa-sha256",signature="pezDShRpsN8sErE5xOPWh4iJOzi2nz7IcwxtCYEQ3KfqezkQIGFb9lxCwgUZDUdueX6R/eUXGJ5S5GdzjEMbs90iIzaqvN2fRmhs3VHgd7Y1O8bMJGfkOz5i6JllyPMgfFT9937147ATmGTmo7h4NhE5XgQ6VwNijNuBhLGOZsCLYqldpuyGuHXyLOmGHsIL/tBf0LBKPMyE6S70hEmh6Zx7nQeW5Kt9CTZIxnSbeFrg2HMiMaFRM5gWtPTH5N6Jr6SGal/vXT9VrN1wjwZI83y45aHmpnU7VImSWGWyXxcveHvspXtDciWAPNU8ng6aglYVgWrLaQz70ApRb10fXg==",headers="date host digest (request-target)"' \ --data \ '{"published":"2024-07-14T21:35:10-07:00","content":"<p><div class=\"e-content\">Luna Nova</div></p>","url":"https://fed.brid.gy/r/https://snarfed.org/2024-07-14_luna-nova-5","actor":"https://fed.brid.gy/snarfed.org","id":"https://fed.brid.gy/r/https://snarfed.org/2024-07-14_luna-nova-5","@context":"https://www.w3.org/ns/activitystreams","type":"Like","object":"https://akko.wtf/objects/e60cda8e-17fb-4b99-b732-fb8a6553d7b2","cc":["https://akko.wtf/users/Oneric","https://www.w3.org/ns/activitystreams#Public","https://akko.wtf/users/Oneric/followers"],"to":["https://www.w3.org/ns/activitystreams#Public"],"content_is_html":true,"contentMap":{"en":"<p><div class=\"e-content\">Luna Nova</div></p>"}}' \ http://localhost:4000/users/Oneric/inbox ``` </details> <details> <summary>Extra logging diff</summary> ```diff diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 6642f7771..7fa69e8e4 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -272,27 +272,35 @@ def outbox(conn, %{"nickname" => nickname}) do end def inbox(%{assigns: %{valid_signature: true}} = conn, %{"nickname" => nickname} = params) do + IO.puts("POST to user inbox with valid sig:\n#{inspect(conn.req_headers)}\n\n#{inspect(params)}\n") with %User{} = recipient <- User.get_cached_by_nickname(nickname), {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(params["actor"]), true <- Utils.recipient_in_message(recipient, actor, params), params <- Utils.maybe_splice_recipient(recipient.ap_id, params) do Federator.incoming_ap_doc(params) json(conn, "ok") + else + e -> + IO.puts("ooopsie: #{inspect(e)}") + {:error, :internal} end end def inbox(%{assigns: %{valid_signature: true}} = conn, params) do + IO.puts("POST to server inbox with valid sig:\n#{inspect(conn.req_headers)}\n\n#{inspect(params)}\n") Federator.incoming_ap_doc(params) json(conn, "ok") end - def inbox(%{assigns: %{valid_signature: false}} = conn, _params) do + def inbox(%{assigns: %{valid_signature: false}} = conn, params) do + IO.puts("POST to an inbox with invalid sig:\n#{inspect(conn.req_headers)}\n\n#{inspect(params)}\n") conn |> put_status(:bad_request) |> json("Invalid HTTP Signature") end - def inbox(conn, _params) do + def inbox(conn, params) do + IO.puts("POST to an inbox _without_ any sig:\n#{inspect(conn.req_headers)}\n\n#{inspect(params)}\n") conn |> put_status(:bad_request) |> json("error, missing HTTP Signature") ``` </details> <details> <summary>Debug log output</summary> ``` POST to user inbox with valid sig: [{"accept", "*/*"}, {"accept-encoding", "gzip, deflate"}, {"connection", "keep-alive"}, {"content-length", "701"}, {"content-type", "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""}, {"date", "Mon, 15 Jul 2024 04:35:16 GMT"}, {"digest", "SHA-256=aD6VWn+4CUs5hBeEjAiSdRjga/h5Qnub0gLVSqSosSk="}, {"host", "akko.wtf"}, {"signature", "keyId=\"https://fed.brid.gy/snarfed.org#key\",algorithm=\"rsa-sha256\",signature=\"pezDShRpsN8sErE5xOPWh4iJOzi2nz7IcwxtCYEQ3KfqezkQIGFb9lxCwgUZDUdueX6R/eUXGJ5S5GdzjEMbs90iIzaqvN2fRmhs3VHgd7Y1O8bMJGfkOz5i6JllyPMgfFT9937147ATmGTmo7h4NhE5XgQ6VwNijNuBhLGOZsCLYqldpuyGuHXyLOmGHsIL/tBf0LBKPMyE6S70hEmh6Zx7nQeW5Kt9CTZIxnSbeFrg2HMiMaFRM5gWtPTH5N6Jr6SGal/vXT9VrN1wjwZI83y45aHmpnU7VImSWGWyXxcveHvspXtDciWAPNU8ng6aglYVgWrLaQz70ApRb10fXg==\",headers=\"date host digest (request-target)\""}, {"user-agent", "Bridgy Fed (https://fed.brid.gy/)"}, {"(request-target)", "post /users/Oneric/inbox"}] %{"@context" => "https://www.w3.org/ns/activitystreams", "actor" => "https://fed.brid.gy/snarfed.org", "cc" => ["https://akko.wtf/users/Oneric", "https://www.w3.org/ns/activitystreams#Public", "https://akko.wtf/users/Oneric/followers"], "content" => "<p><div class=\"e-content\">Luna Nova</div></p>", "contentMap" => %{"en" => "<p><div class=\"e-content\">Luna Nova</div></p>"}, "content_is_html" => true, "id" => "https://fed.brid.gy/r/https://snarfed.org/2024-07-14_luna-nova-5", "nickname" => "Oneric", "object" => "https://akko.wtf/objects/e60cda8e-17fb-4b99-b732-fb8a6553d7b2", "published" => "2024-07-14T21:35:10-07:00", "to" => ["https://www.w3.org/ns/activitystreams#Public"], "type" => "Like", "url" => "https://fed.brid.gy/r/https://snarfed.org/2024-07-14_luna-nova-5"} ooopsie: false ``` </details> **EDIT:** Perhaps worth noting i replaced some double backslashes `\\` in the originally posted request with a single backslash `\` each, else it wasn’t valid JSON (and gave a different error). I’m assuming this is just an artefact of whatever this was copied from
Author

Thanks again for looking at this!

Are you sure this really exactly matches what other servers receive; Does some intermediate framework or proxy maybe alter something?

Good question. It's possible that the hosting platform I'm on (Google App Engine) adds headers - which shouldn't change sig verification, right? - but I'm pretty sure it doesn't modify existing headers or the body.

EDIT: Perhaps worth noting i replaced some double backslashes \ in the originally posted request with a single backslash \ each, else it wasn’t valid JSON (and gave a different error). I’m assuming this is just an artefact of whatever this was copied from

Good catch, thanks, sorry about that!

Thanks again for looking at this! > Are you sure this really exactly matches what other servers receive; Does some intermediate framework or proxy maybe alter something? Good question. It's possible that the hosting platform I'm on (Google App Engine) _adds_ headers - which shouldn't change sig verification, right? - but I'm pretty sure it doesn't modify existing headers or the body. > EDIT: Perhaps worth noting i replaced some double backslashes \\ in the originally posted request with a single backslash \ each, else it wasn’t valid JSON (and gave a different error). I’m assuming this is just an artefact of whatever this was copied from Good catch, thanks, sorry about that!
Author

...actually, this request is sent over TLS, and I think my stack does the encryption and constructs the HTTP request, so App Engine shouldn't be able to alter or add anything.

...actually, this request is sent over TLS, and I think my stack does the encryption and constructs the HTTP request, so App Engine shouldn't be able to alter or add anything.
Member

Can you set up a temporary ap_logger (see previous messages) instance? This way you’ll be able to collect the actual request representation on the receiving end and compare it to what you expected

Can you set up a temporary ap_logger *(see previous messages)* instance? This way you’ll be able to collect the actual request representation on the receiving end and compare it to what you expected
Author

Sure! I've attached an ap_logger log of another inbox delivery of a Like. Looks like it's missing one header that the previous request had, Connection. I'm guessing that's HTTP 2 vs 1.1, or network path specific or something similar.

Sure! I've attached an ap_logger log of another inbox delivery of a `Like`. Looks like it's missing one header that the previous request had, `Connection`. I'm guessing that's HTTP 2 vs 1.1, or network path specific or something similar.
2.1 KiB
Member

Looks like it's missing one header that the previous request had, Connection. I'm guessing that's HTTP 2 vs 1.1, or network path specific or something similar.

Capitalisation of Header names is also different though i’m not sure if that’s due to ap_loggers http lib

Do the contents of all other headers, e.g. Date, also match what you expected/seen on the sender site? Ideally we’d get the same request logged from both the sender and receiver side

> Looks like it's missing one header that the previous request had, Connection. I'm guessing that's HTTP 2 vs 1.1, or network path specific or something similar. Capitalisation of Header names is also different though i’m not sure if that’s due to ap_loggers http lib Do the contents of all other headers, e.g. `Date`, also match what you expected/seen on the sender site? Ideally we’d get the same request logged from both the sender and receiver side
Author

Here's my record of that request from the sender side. Apart from header capitalization, looks like everything's the same, including Date.

POST /actors/person/inbox HTTP/1.1

user-agent: Bridgy Fed (https://fed.brid.gy/)
accept-encoding: gzip, deflate
accept: */*
connection: keep-alive
date: Mon, 15 Jul 2024 20:48:03 GMT
host: 02cd-135-180-111-81.ngrok-free.app
content-type: application/ld+json; profile="https://www.w3.org/ns/activitystreams"
digest: SHA-256=Z76uYZJzCbh+nEdB5tTixXOnWSBDcHVsB2VoXj8OtvU=
Content-Length: 776
signature: keyId="https://fed.brid.gy/snarfed.org#key",algorithm="rsa-sha256",signature="ccAkA8zSKxmAQ+czHoDtBIveTiFKg/NSPmJc/r1CyaHlWfOg0lTpSpALb86we77i9NEhO6QgTbC5hVhrgb4F1OdHrfpN5apgFQbLYQB8rVU6PyV7AGf705cfIdhSa16+eyZ0/seZOg3SlpIZmulNq6MAKxb0tx9uFPkH+bNAqD9xIyyL9moBBqB7hT6Yl02s//SV56eHw6AtxVLX6COdoDENFINeAsjWL0sLI4S2a9mb+MpZ/e4tmL9lWNWIwQJE3kPEhuZbGms/lB63dabFc5e477yxLiUCuTEbgLdMXrl08NP1fPoWEdEQ4ug7IOAbzg4sNJ4oGSdVqJZUWWPNJQ==",headers="date host digest (request-target)"

{"published":"2024-07-15T13:47:58-07:00","content":"<p><div class=\"e-content\">02cd-135-180-111-81.ngrok-free.app/</div></p>","url":"https://fed.brid.gy/r/https://snarfed.org/2024-07-15_02cd-135-180-111-81-ngrok-free-app","actor":"https://fed.brid.gy/snarfed.org","id":"https://fed.brid.gy/r/https://snarfed.org/2024-07-15_02cd-135-180-111-81-ngrok-free-app","@context":"https://www.w3.org/ns/activitystreams","type":"Like","object":"http://02cd-135-180-111-81.ngrok-free.app/objects/note","cc":["http://02cd-135-180-111-81.ngrok-free.app/actors/person","https://www.w3.org/ns/activitystreams#Public"],"to":["https://www.w3.org/ns/activitystreams#Public"],"content_is_html":true,"contentMap":{"en":"<p><div class=\"e-content\">02cd-135-180-111-81.ngrok-free.app/</div></p>"}}
Here's my record of that request from the sender side. Apart from header capitalization, looks like everything's the same, including `Date`. ``` POST /actors/person/inbox HTTP/1.1 user-agent: Bridgy Fed (https://fed.brid.gy/) accept-encoding: gzip, deflate accept: */* connection: keep-alive date: Mon, 15 Jul 2024 20:48:03 GMT host: 02cd-135-180-111-81.ngrok-free.app content-type: application/ld+json; profile="https://www.w3.org/ns/activitystreams" digest: SHA-256=Z76uYZJzCbh+nEdB5tTixXOnWSBDcHVsB2VoXj8OtvU= Content-Length: 776 signature: keyId="https://fed.brid.gy/snarfed.org#key",algorithm="rsa-sha256",signature="ccAkA8zSKxmAQ+czHoDtBIveTiFKg/NSPmJc/r1CyaHlWfOg0lTpSpALb86we77i9NEhO6QgTbC5hVhrgb4F1OdHrfpN5apgFQbLYQB8rVU6PyV7AGf705cfIdhSa16+eyZ0/seZOg3SlpIZmulNq6MAKxb0tx9uFPkH+bNAqD9xIyyL9moBBqB7hT6Yl02s//SV56eHw6AtxVLX6COdoDENFINeAsjWL0sLI4S2a9mb+MpZ/e4tmL9lWNWIwQJE3kPEhuZbGms/lB63dabFc5e477yxLiUCuTEbgLdMXrl08NP1fPoWEdEQ4ug7IOAbzg4sNJ4oGSdVqJZUWWPNJQ==",headers="date host digest (request-target)" {"published":"2024-07-15T13:47:58-07:00","content":"<p><div class=\"e-content\">02cd-135-180-111-81.ngrok-free.app/</div></p>","url":"https://fed.brid.gy/r/https://snarfed.org/2024-07-15_02cd-135-180-111-81-ngrok-free-app","actor":"https://fed.brid.gy/snarfed.org","id":"https://fed.brid.gy/r/https://snarfed.org/2024-07-15_02cd-135-180-111-81-ngrok-free-app","@context":"https://www.w3.org/ns/activitystreams","type":"Like","object":"http://02cd-135-180-111-81.ngrok-free.app/objects/note","cc":["http://02cd-135-180-111-81.ngrok-free.app/actors/person","https://www.w3.org/ns/activitystreams#Public"],"to":["https://www.w3.org/ns/activitystreams#Public"],"content_is_html":true,"contentMap":{"en":"<p><div class=\"e-content\">02cd-135-180-111-81.ngrok-free.app/</div></p>"}} ```
Member

Thanks! Unfortunately this didn’t bring me any closer to figuring out what’s going on though.

I checked again and regardless of what happens before, Phoenix/Plug will always lowercase header names anyway.
A tweaked request version still passes signature verification like before.
Even enabling authoriued_fetch and putting (plain-HTTP) nginx in front of my local test server doesn’t change anything about signature verification

To check i also sent the same curl request to real akko.wtf, but unlike on localhost it fails with a "no signature error" (which might also happen if the signature was invalid and auth-fetch is enabled iiuc).
Since this error apparently persists across all Akkoma instances it also doesn’t seem to be a misconfiguration specific to akko.wtf. Perhaps try interacting with an Akkoma instance which does not require authfetch to get another data point, e.g.: https://fedi.absturztau.be

A curlified full, signed request is quite helpful, but I’m afraid further debugging needs direct access to an affected live server setup which I don’t have

Thanks! Unfortunately this didn’t bring me any closer to figuring out what’s going on though. I checked again and regardless of what happens before, Phoenix/Plug will always lowercase header names anyway. A tweaked request version still passes signature verification like before. Even enabling `authoriued_fetch` and putting (plain-HTTP) nginx in front of my local test server doesn’t change anything about signature verification To check i also sent the same curl request to real akko.wtf, but unlike on localhost it fails with a "no signature error" (which might also happen if the signature was invalid and auth-fetch is enabled iiuc). Since this error apparently persists across all Akkoma instances it also doesn’t seem to be a misconfiguration specific to akko.wtf. Perhaps try interacting with an Akkoma instance which does _not_ require authfetch to get another data point, e.g.: https://fedi.absturztau.be A `curl`ified full, signed request is quite helpful, but I’m afraid further debugging needs direct access to an affected live server setup which I don’t have
Contributor

Fow whatever it's worth I've had AF off when trying to use the bridge so I'm not sure it's AF-related 🤷🏻‍♀️

Fow whatever it's worth I've had AF off when trying to use the bridge so I'm not sure it's AF-related 🤷🏻‍♀️
Author

I successfully sent a Like to https://fedi.absturztau.be/notice/AjzA414GNoB0Wh1g4u via Bridgy Fed just now!

So...maybe it's AF after all?

I successfully sent a `Like` to https://fedi.absturztau.be/notice/AjzA414GNoB0Wh1g4u via Bridgy Fed just now! So...maybe it's AF after all?
Contributor

AF is off on mine

That's great! Maybe something else has changed because I've had AF off for quite a while. My instance name is Idiomdrottning.

I'd love to get bridged https://bsky.app/profile/Sandra.idiomdrottning.org.ap.brid.gy still shows 0

![AF is off on mine](/attachments/cea0aaaa-41af-4245-be9a-15790338d0d7) That's great! Maybe something else has changed because I've had AF off for quite a while. My instance name is [Idiomdrottning](https://idiomdrottning.org/main/bubble). I'd love to get bridged https://bsky.app/profile/Sandra.idiomdrottning.org.ap.brid.gy still shows 0
Author

Ah, looks like you hit one of BF's spam filters, it currently expects that your display name is different from your username: https://fed.brid.gy/docs#troubleshooting

That requirement probably has too many false positives, I should remove it!

Ah, looks like you hit one of BF's spam filters, it currently expects that your display name is different from your username: https://fed.brid.gy/docs#troubleshooting That requirement probably has too many false positives, I should remove it!
Author

Done! @snan try unfollowing and re-following @bsky.brid.gy@bsky.brid.gy

Done! @snan try unfollowing and re-following @bsky.brid.gy@bsky.brid.gy
Author

@Oneric, not sure where this leaves us. AF seems like maybe a lead, but if so, I don't understand why. BF is happily fetching objects from akko.wtf (eg https://akko.wtf/objects/e60cda8e-17fb-4b99-b732-fb8a6553d7b2 ) with GETs signed with keyId=https://fed.brid.gy/fed.brid.gy#key.

Here are some of the other data points so far afaict:

  • BF has sent a Like to fedi.absturztau.be successfully
  • ...but when it tries to deliver a Like to akko.wtf, it gets HTTP 401 ... Request not signed back
  • You think the fragment in keyId isn't the issue, which makes sense, since other big projects like Mastodon also have fragments in their keyIds
  • Looking at a similar example inbox delivery POST from Bridgy Fed, Akkoma verified the signature ok locally for you, and you think akko.wtf is running basically the same Akkoma version
  • Apart from the signature, Akkoma seems to process the activity itself ok

...so maybe AF is still our best lead, even if we don't understand why...?

@Oneric, not sure where this leaves us. AF seems like maybe a lead, but if so, I don't understand why. BF is happily fetching objects from akko.wtf (eg https://akko.wtf/objects/e60cda8e-17fb-4b99-b732-fb8a6553d7b2 ) with GETs signed with `keyId=https://fed.brid.gy/fed.brid.gy#key`. Here are some of the other data points so far afaict: * BF has sent a `Like` to fedi.absturztau.be successfully * ...but when it tries to deliver a `Like` to akko.wtf, it gets `HTTP 401 ... Request not signed` back * [You think the fragment in `keyId` isn't the issue](https://akkoma.dev/AkkomaGang/akkoma/issues/438#issuecomment-12017), which makes sense, since other big projects like Mastodon also have fragments in their `keyId`s * Looking at a similar example inbox delivery POST from Bridgy Fed, Akkoma verified the signature ok locally for you, and you think akko.wtf is running basically the same Akkoma version * Apart from the signature, Akkoma seems to process the activity itself ok ...so maybe AF is still our best lead, even if we don't understand why...?
Contributor

Done! @snan try unfollowing and re-following @bsky.brid.gy@bsky.brid.gy

🧎🏻‍♀️

> Done! @snan try unfollowing and re-following @bsky.brid.gy@bsky.brid.gy 🧎🏻‍♀️
Member

...so maybe AF is still our best lead, even if we don't understand why...?

I’ve just rechecked this and made sure the setting applied correctly, but as mentioned before the sample request still works for me locally even with authfetch enabled. Authfetch might be a necessary but apparently not the only component here.

There are two places reacting to authorized_fetch_mode

  • ensure_http_signatures_plug which ends up rejecting the request with a not quite accurate "no signature" message (same message for no and invalid siganture)
  • mapping key id to actor checks whether the actor domain is blocked only when authfetch is enabled (if it’s blocked the signature, is marked as invalid and a message logged)
    (i know without authfetch domain blocks can't be reliable enforced on access, but idk why it isn’t even trying without authfetch)

iirc (server currently down due to power issues) akko.wtf at least doesn’t disclose a block for fedi.brid.gy, although bsky.brid.gy was (or maybe still is if lifting was forgotten) temporarily blocked due to technical issues early on.
ihatebeinga.live doesn’t disclose a block for any *.brid.gy domain yet was also affected.

Someone with direct admin access to an affected instance will need to take a closer look at this.

Possibly helpful extra debug logging patch
diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
index 6642f7771..6762a968c 100644
--- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
@@ -272,27 +272,35 @@ def outbox(conn, %{"nickname" => nickname}) do
   end
 
   def inbox(%{assigns: %{valid_signature: true}} = conn, %{"nickname" => nickname} = params) do
+    Logger.info("POST to user inbox with valid sig:\n#{inspect(conn.req_headers)}\n\n#{inspect(params)}\n")
     with %User{} = recipient <- User.get_cached_by_nickname(nickname),
          {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(params["actor"]),
          true <- Utils.recipient_in_message(recipient, actor, params),
          params <- Utils.maybe_splice_recipient(recipient.ap_id, params) do
       Federator.incoming_ap_doc(params)
       json(conn, "ok")
+    else
+      e ->
+        Logger.info("ooopsie: #{inspect(e)}")
+        {:error, :internal}
     end
   end
 
   def inbox(%{assigns: %{valid_signature: true}} = conn, params) do
+    Logger.info("POST to server inbox with valid sig:\n#{inspect(conn.req_headers)}\n\n#{inspect(params)}\n")
     Federator.incoming_ap_doc(params)
     json(conn, "ok")
   end
 
-  def inbox(%{assigns: %{valid_signature: false}} = conn, _params) do
+  def inbox(%{assigns: %{valid_signature: false}} = conn, params) do
+    Logger.info("POST to an inbox with invalid sig:\n#{inspect(conn.req_headers)}\n\n#{inspect(params)}\n")
     conn
     |> put_status(:bad_request)
     |> json("Invalid HTTP Signature")
   end
 
-  def inbox(conn, _params) do
+  def inbox(conn, params) do
+    Logger.info("POST to an inbox _without_ any sig:\n#{inspect(conn.req_headers)}\n\n#{inspect(params)}\n")
     conn
     |> put_status(:bad_request)
     |> json("error, missing HTTP Signature")
diff --git a/lib/pleroma/web/plugs/ensure_http_signature_plug.ex b/lib/pleroma/web/plugs/ensure_http_signature_plug.ex
index c75501a2d..7389e1478 100644
--- a/lib/pleroma/web/plugs/ensure_http_signature_plug.ex
+++ b/lib/pleroma/web/plugs/ensure_http_signature_plug.ex
@@ -11,15 +11,32 @@ defmodule Pleroma.Web.Plugs.EnsureHTTPSignaturePlug do
 
   alias Pleroma.Config
 
+  require Logger
+
   def init(options) do
     options
   end
 
   def call(%{assigns: %{valid_signature: true}} = conn, _), do: conn
 
-  def call(conn, _) do
+  def call(%{assigns: %{valid_signature: false}} = conn, params) do
+    maybe_drop_conn(
+      conn,
+      "Dropping invalid sig request:\n#{inspect(conn.req_headers)}\n\n#{inspect(params)}\n}"
+    )
+  end
+
+  def call(conn, params) do
+    maybe_drop_conn(
+      conn,
+      "Dropping unauthed request:\n#{inspect(conn.req_headers)}\n\n#{inspect(params)}\n}"
+    )
+  end
+
+  def maybe_drop_conn(conn, dbgmsg) do
     with true <- get_format(conn) in ["json", "activity+json"],
          true <- Config.get([:activitypub, :authorized_fetch_mode], true) do
+	  Logger.info(dbgmsg)
       conn
       |> put_status(:unauthorized)
       |> text("Request not signed")
diff --git a/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex b/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex
index a73def682..f2bdbf792 100644
--- a/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex
+++ b/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex
@@ -26,23 +26,23 @@ def call(%{assigns: %{valid_signature: true}, params: %{"actor" => actor}} = con
       |> AuthHelper.skip_oauth()
     else
       {:user_match, false} ->
-        Logger.debug("Failed to map identity from signature (payload actor mismatch)")
-        Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{inspect(actor)}")
+        Logger.info("Failed to map identity from signature (payload actor mismatch)")
+        Logger.info("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{inspect(actor)}")
 
         conn
         |> assign(:valid_signature, false)
 
       # remove me once testsuite uses mapped capabilities instead of what we do now
       {:user, nil} ->
-        Logger.debug("Failed to map identity from signature (lookup failure)")
-        Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{actor}")
+        Logger.info("Failed to map identity from signature (lookup failure)")
+        Logger.info("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{actor}")
 
         conn
         |> assign(:valid_signature, false)
 
       {:federate, false} ->
-        Logger.debug("Identity from signature is instance blocked")
-        Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{actor}")
+        Logger.info("Identity from signature is instance blocked")
+        Logger.info("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{actor}")
 
         conn
         |> assign(:valid_signature, false)
@@ -58,21 +58,21 @@ def call(%{assigns: %{valid_signature: true}} = conn, _opts) do
       |> AuthHelper.skip_oauth()
     else
       {:federate, false} ->
-        Logger.debug("Identity from signature is instance blocked")
-        Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}")
+        Logger.info("Identity from signature is instance blocked")
+        Logger.info("key_id=#{inspect(key_id_from_conn(conn))}")
 
         conn
         |> assign(:valid_signature, false)
 
       nil ->
-        Logger.debug("Failed to map identity from signature (lookup failure)")
-        Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}")
+        Logger.info("Failed to map identity from signature (lookup failure)")
+        Logger.info("key_id=#{inspect(key_id_from_conn(conn))}")
 
         only_permit_user_routes(conn)
 
       _ ->
-        Logger.debug("Failed to map identity from signature (no payload actor mismatch)")
-        Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}")
+        Logger.info("Failed to map identity from signature (no payload actor mismatch)")
+        Logger.info("key_id=#{inspect(key_id_from_conn(conn))}")
 
         conn
         |> assign(:valid_signature, false)
> ...so maybe AF is still our best lead, even if we don't understand why...? I’ve just rechecked this and made sure the setting applied correctly, but as mentioned before the sample request still works for me locally even _with_ authfetch enabled. Authfetch might be a necessary but apparently not the only component here. There are two places reacting to `authorized_fetch_mode` - `ensure_http_signatures_plug` which ends up rejecting the request with a not quite accurate "no signature" message (same message for no and invalid siganture) - mapping key id to actor checks whether the actor domain is blocked only when authfetch is enabled *(if it’s blocked the signature, is marked as invalid and a message logged)* *(i know without authfetch domain blocks can't be reliable enforced on access, but idk why it isn’t even trying without authfetch)* iirc (server currently down due to power issues) akko.wtf at least doesn’t disclose a block for `fedi.brid.gy`, although bsky.brid.gy was *(or maybe still is if lifting was forgotten)* temporarily blocked due to technical issues early on. ihatebeinga.live doesn’t disclose a block for any `*.brid.gy` domain yet was also affected. Someone with direct admin access to an affected instance will need to take a closer look at this. <details> <summary>Possibly helpful extra debug logging patch</summary> ```diff diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 6642f7771..6762a968c 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -272,27 +272,35 @@ def outbox(conn, %{"nickname" => nickname}) do end def inbox(%{assigns: %{valid_signature: true}} = conn, %{"nickname" => nickname} = params) do + Logger.info("POST to user inbox with valid sig:\n#{inspect(conn.req_headers)}\n\n#{inspect(params)}\n") with %User{} = recipient <- User.get_cached_by_nickname(nickname), {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(params["actor"]), true <- Utils.recipient_in_message(recipient, actor, params), params <- Utils.maybe_splice_recipient(recipient.ap_id, params) do Federator.incoming_ap_doc(params) json(conn, "ok") + else + e -> + Logger.info("ooopsie: #{inspect(e)}") + {:error, :internal} end end def inbox(%{assigns: %{valid_signature: true}} = conn, params) do + Logger.info("POST to server inbox with valid sig:\n#{inspect(conn.req_headers)}\n\n#{inspect(params)}\n") Federator.incoming_ap_doc(params) json(conn, "ok") end - def inbox(%{assigns: %{valid_signature: false}} = conn, _params) do + def inbox(%{assigns: %{valid_signature: false}} = conn, params) do + Logger.info("POST to an inbox with invalid sig:\n#{inspect(conn.req_headers)}\n\n#{inspect(params)}\n") conn |> put_status(:bad_request) |> json("Invalid HTTP Signature") end - def inbox(conn, _params) do + def inbox(conn, params) do + Logger.info("POST to an inbox _without_ any sig:\n#{inspect(conn.req_headers)}\n\n#{inspect(params)}\n") conn |> put_status(:bad_request) |> json("error, missing HTTP Signature") diff --git a/lib/pleroma/web/plugs/ensure_http_signature_plug.ex b/lib/pleroma/web/plugs/ensure_http_signature_plug.ex index c75501a2d..7389e1478 100644 --- a/lib/pleroma/web/plugs/ensure_http_signature_plug.ex +++ b/lib/pleroma/web/plugs/ensure_http_signature_plug.ex @@ -11,15 +11,32 @@ defmodule Pleroma.Web.Plugs.EnsureHTTPSignaturePlug do alias Pleroma.Config + require Logger + def init(options) do options end def call(%{assigns: %{valid_signature: true}} = conn, _), do: conn - def call(conn, _) do + def call(%{assigns: %{valid_signature: false}} = conn, params) do + maybe_drop_conn( + conn, + "Dropping invalid sig request:\n#{inspect(conn.req_headers)}\n\n#{inspect(params)}\n}" + ) + end + + def call(conn, params) do + maybe_drop_conn( + conn, + "Dropping unauthed request:\n#{inspect(conn.req_headers)}\n\n#{inspect(params)}\n}" + ) + end + + def maybe_drop_conn(conn, dbgmsg) do with true <- get_format(conn) in ["json", "activity+json"], true <- Config.get([:activitypub, :authorized_fetch_mode], true) do + Logger.info(dbgmsg) conn |> put_status(:unauthorized) |> text("Request not signed") diff --git a/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex b/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex index a73def682..f2bdbf792 100644 --- a/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex +++ b/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex @@ -26,23 +26,23 @@ def call(%{assigns: %{valid_signature: true}, params: %{"actor" => actor}} = con |> AuthHelper.skip_oauth() else {:user_match, false} -> - Logger.debug("Failed to map identity from signature (payload actor mismatch)") - Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{inspect(actor)}") + Logger.info("Failed to map identity from signature (payload actor mismatch)") + Logger.info("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{inspect(actor)}") conn |> assign(:valid_signature, false) # remove me once testsuite uses mapped capabilities instead of what we do now {:user, nil} -> - Logger.debug("Failed to map identity from signature (lookup failure)") - Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{actor}") + Logger.info("Failed to map identity from signature (lookup failure)") + Logger.info("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{actor}") conn |> assign(:valid_signature, false) {:federate, false} -> - Logger.debug("Identity from signature is instance blocked") - Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{actor}") + Logger.info("Identity from signature is instance blocked") + Logger.info("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{actor}") conn |> assign(:valid_signature, false) @@ -58,21 +58,21 @@ def call(%{assigns: %{valid_signature: true}} = conn, _opts) do |> AuthHelper.skip_oauth() else {:federate, false} -> - Logger.debug("Identity from signature is instance blocked") - Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}") + Logger.info("Identity from signature is instance blocked") + Logger.info("key_id=#{inspect(key_id_from_conn(conn))}") conn |> assign(:valid_signature, false) nil -> - Logger.debug("Failed to map identity from signature (lookup failure)") - Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}") + Logger.info("Failed to map identity from signature (lookup failure)") + Logger.info("key_id=#{inspect(key_id_from_conn(conn))}") only_permit_user_routes(conn) _ -> - Logger.debug("Failed to map identity from signature (no payload actor mismatch)") - Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}") + Logger.info("Failed to map identity from signature (no payload actor mismatch)") + Logger.info("key_id=#{inspect(key_id_from_conn(conn))}") conn |> assign(:valid_signature, false) ``` </details>
Contributor

Done! @snan try unfollowing and re-following @bsky.brid.gy@bsky.brid.gy

Still didn't fly 🤷🏻‍♀️ Only says "request sent".

> Done! @snan try unfollowing and re-following @bsky.brid.gy@bsky.brid.gy Still didn't fly 🤷🏻‍♀️ Only says "request sent".
Author

Still didn't fly 🤷🏻‍♀️ Only says "request sent".

Looks like you're now hitting this same Akkoma issue. Bridgy Fed sent both an Accept and a Follow from @bsky.brid.gy@bsky.brid.gy to https://idiomdrottning.org/users/Sandra/inbox , and it got 500 Server Error: Internal Server Error responses for both.

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Accept",
  "id": "https://fed.brid.gy/r/bsky.brid.gy/followers#accept-https://idiomdrottning.org/activities/ce6ea4e7-4787-4a02-9daf-0bab833f5809",
  "actor": "https://bsky.brid.gy/bsky.brid.gy",
  "object": {
    "type": "Follow",
    "id": "https://idiomdrottning.org/activities/ce6ea4e7-4787-4a02-9daf-0bab833f5809",
    "url": "https://idiomdrottning.org/users/Sandra#followed-bsky.brid.gy",
    "actor": "https://idiomdrottning.org/users/Sandra",
    "state": "pending",
    "object": "https://bsky.brid.gy/bsky.brid.gy",
    "to": [
      "bsky.brid.gy",
      "https://www.w3.org/ns/activitystreams#Public"
    ]
  },
  "to": ["https://www.w3.org/ns/activitystreams#Public"]
}

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Follow",
  "id": "https://bsky.brid.gy/r/https://bsky.brid.gy/#follow-back-https://idiomdrottning.org/users/Sandra-2024-07-30T09:26:05.256763+00:00",
  "actor": "https://bsky.brid.gy/bsky.brid.gy",
  "object": "https://idiomdrottning.org/users/Sandra",
  "to": ["https://www.w3.org/ns/activitystreams#Public"]
}
> Still didn't fly 🤷🏻‍♀️ Only says "request sent". Looks like you're now hitting this same Akkoma issue. Bridgy Fed sent both an `Accept` and a `Follow` from `@bsky.brid.gy@bsky.brid.gy` to https://idiomdrottning.org/users/Sandra/inbox , and it got `500 Server Error: Internal Server Error` responses for both. ```json { "@context": "https://www.w3.org/ns/activitystreams", "type": "Accept", "id": "https://fed.brid.gy/r/bsky.brid.gy/followers#accept-https://idiomdrottning.org/activities/ce6ea4e7-4787-4a02-9daf-0bab833f5809", "actor": "https://bsky.brid.gy/bsky.brid.gy", "object": { "type": "Follow", "id": "https://idiomdrottning.org/activities/ce6ea4e7-4787-4a02-9daf-0bab833f5809", "url": "https://idiomdrottning.org/users/Sandra#followed-bsky.brid.gy", "actor": "https://idiomdrottning.org/users/Sandra", "state": "pending", "object": "https://bsky.brid.gy/bsky.brid.gy", "to": [ "bsky.brid.gy", "https://www.w3.org/ns/activitystreams#Public" ] }, "to": ["https://www.w3.org/ns/activitystreams#Public"] } { "@context": "https://www.w3.org/ns/activitystreams", "type": "Follow", "id": "https://bsky.brid.gy/r/https://bsky.brid.gy/#follow-back-https://idiomdrottning.org/users/Sandra-2024-07-30T09:26:05.256763+00:00", "actor": "https://bsky.brid.gy/bsky.brid.gy", "object": "https://idiomdrottning.org/users/Sandra", "to": ["https://www.w3.org/ns/activitystreams#Public"] } ```
Contributor

@snarfed That's great, good to know that there's not any AF or username issues and that it's instead this mysterious issue. Thank you so much for all your hard work on this bridge. 🙏🏻

@snarfed That's great, good to know that there's not any AF or username issues and that it's instead this mysterious issue. Thank you so much for all your hard work on this bridge. 🙏🏻

For what it's worth, I just ran into a very similar issue with my own AP software.
What I discovered is that akkoma makes a request to the actor's key url with an Accept header of application/ld+json; profile="https://www.w3.org/ns/activitystreams" - if the Content-Type of the response is not exactly this value, (at least, it needs to contain the base content type application/ld+json and the profile), the signature is rejected regardless of the response content.

For what it's worth, I just ran into a *very* similar issue with my own AP software. What I discovered is that akkoma makes a request to the actor's key url with an `Accept` header of `application/ld+json; profile="https://www.w3.org/ns/activitystreams"` - if the `Content-Type` of the response is not *exactly* this value, (at least, it needs to contain the base content type `application/ld+json` *and* the profile), the signature is rejected regardless of the response content.
Member

What I discovered is that akkoma makes a request to the actor's key url with an Accept header of application/ld+json; profile="https://www.w3.org/ns/activitystreams"

This is required by ActivityPub spec; using any Accept header which doesn’t contain this content type to request AP objects is violating spec.
Note *oma historically used a different header which is also tolerated by most implementations and this issue was filed before the header was fixed.

  • if the Content-Type of the response is not exactly this value [...] the signature is rejected regardless of the response content.

I believe this is incorrect. All of the following return conten types are accepted:

  • application/ld+json with the AS profile (atm only if AS is the sole profile; after #814 multiple profiles are allowed if AS in one of them)
    Spec requires servers to present their AP objects with this Content-Type; if you don’t you’re breaking spec
  • application/activity+json which the spec suggests but doesn’t require to be accepted. Notably Mastodon uses this type for its objects

In particular accepting JSON-LD without the AS profile is out of question since
a. we are notable to do JSON-LD processing and rely on the representation guarantees given by the AS profile
b. JSON-LD is used outside of ActivityPub so the returned object meight not even be intended to be a valid AP object but be e.g. a user uploaded file. We cannot accept this for security reasons

> What I discovered is that akkoma makes a request to the actor's key url with an Accept header of application/ld+json; profile="https://www.w3.org/ns/activitystreams" This is _required_ by ActivityPub spec; using any Accept header which doesn’t contain this content type to request AP objects is violating spec. Note \*oma historically used a different header which is also tolerated by most implementations and this issue was filed before the header was fixed. > - if the Content-Type of the response is not exactly this value [...] the signature is rejected regardless of the response content. I believe this is incorrect. All of the following return conten types are accepted: - `application/ld+json` with the AS profile *(atm only if AS is the sole profile; after #814 multiple profiles are allowed if AS in one of them)* Spec _requires_ servers to present their AP objects with this Content-Type; if you don’t you’re breaking spec - `application/activity+json` which the spec suggests but doesn’t require to be accepted. Notably Mastodon uses this type for its objects In particular accepting JSON-LD _without_ the AS profile is out of question since a. we are notable to do JSON-LD processing and rely on the representation guarantees given by the AS profile b. JSON-LD is used outside of ActivityPub so the returned object meight not even be intended to be a valid AP object but be e.g. a user uploaded file. We cannot accept this for security reasons

It may be incorrect, but it's not true - previously, I was returning content type application/activity+json, and in the process of debugging I also tried returning application/ld+json with no profile, both of which are rejected by akkoma.

(I'm fixing this in my software, and I'm not implying that akkoma is behaving incorrectly here - I just made this note to maybe help debugging the bridgy issue, since I came across this while trying to debug mine)

It may be *incorrect*, but it's not *true* - previously, I was returning content type `application/activity+json`, and in the process of debugging I also tried returning `application/ld+json` with no profile, both of which are rejected by akkoma. (I'm fixing this in my software, and I'm not implying that akkoma is behaving incorrectly here - I just made this note to maybe help debugging the bridgy issue, since I came across this while trying to debug mine)
Member

application/activity+json is explicitly accepted in the code and Mastodon returns application/activity+json while working fine with Akkoma. I’m fairly sure — while incorrect per spec — declaring AP objects as application/activity+json doesn't break anything wrt federation with Akkoma.

`application/activity+json` is explicitly accepted in [the code](https://akkoma.dev/AkkomaGang/akkoma/src/commit/3bb31117e65079d710e5129eb70c946d4ffe99ff/lib/pleroma/object/fetcher.ex#L368-L377) and Mastodon returns `application/activity+json` while working fine with Akkoma. I’m fairly sure — while incorrect per spec — declaring AP objects as `application/activity+json` doesn't break anything wrt federation with Akkoma.
Sign in to join this conversation.
No milestone
No project
No assignees
6 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: AkkomaGang/akkoma#438
No description provided.