fix(federation): prevent fetching own relay actor

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2023-06-06 09:52:46 +02:00
parent 99db295310
commit b981f91cf7
No known key found for this signature in database
GPG key ID: A061B9DDE0CA0773
2 changed files with 52 additions and 30 deletions

View file

@ -22,7 +22,9 @@ defmodule Mobilizon.Federation.ActivityPub.Fetcher do
| {:error, | {:error,
:invalid_url | :http_gone | :http_error | :http_not_found | :content_not_json} :invalid_url | :http_gone | :http_error | :http_not_found | :content_not_json}
def fetch(url, options \\ []) do def fetch(url, options \\ []) do
on_behalf_of = Keyword.get(options, :on_behalf_of, Relay.get_actor()) Logger.debug("Fetching #{url} with AP Fetcher")
on_behalf_of = Keyword.get(options, :on_behalf_of, actor_relay())
Logger.debug("Fetching on behalf of #{inspect(on_behalf_of.url)}")
date = Signature.generate_date_header() date = Signature.generate_date_header()
headers = headers =
@ -32,29 +34,8 @@ defmodule Mobilizon.Federation.ActivityPub.Fetcher do
client = ActivityPubClient.client(headers: headers) client = ActivityPubClient.client(headers: headers)
if address_valid?(url) do if address_valid?(url) and url != on_behalf_of.url do
case ActivityPubClient.get(client, url) do do_fetch(url, client)
{:ok, %Tesla.Env{body: data, status: code}} when code in 200..299 and is_map(data) ->
{:ok, data}
{:ok, %Tesla.Env{status: 410}} ->
Logger.debug("Resource at #{url} is 410 Gone")
{:error, :http_gone}
{:ok, %Tesla.Env{status: 404}} ->
Logger.debug("Resource at #{url} is 404 Gone")
{:error, :http_not_found}
{:ok, %Tesla.Env{body: data}} when is_binary(data) ->
{:error, :content_not_json}
{:ok, %Tesla.Env{} = res} ->
Logger.debug("Resource returned bad HTTP code #{inspect(res)}")
{:error, :http_error}
{:error, err} ->
{:error, err}
end
else else
{:error, :invalid_url} {:error, :invalid_url}
end end
@ -169,4 +150,38 @@ defmodule Mobilizon.Federation.ActivityPub.Fetcher do
%URI{host: host, scheme: scheme} = URI.parse(address) %URI{host: host, scheme: scheme} = URI.parse(address)
is_valid_string(host) and is_valid_string(scheme) is_valid_string(host) and is_valid_string(scheme)
end end
defp actor_relay do
Relay.get_actor()
end
@spec do_fetch(String.t(), Tesla.Client.t()) ::
{:ok, map()}
| {:error,
:invalid_url | :http_gone | :http_error | :http_not_found | :content_not_json}
defp do_fetch(url, client) do
case ActivityPubClient.get(client, url) do
{:ok, %Tesla.Env{body: data, status: code}} when code in 200..299 and is_map(data) ->
Logger.debug("Found the following from ActivityPubClient fetch: #{inspect(data)}")
{:ok, data}
{:ok, %Tesla.Env{status: 410}} ->
Logger.debug("Resource at #{url} is 410 Gone")
{:error, :http_gone}
{:ok, %Tesla.Env{status: 404}} ->
Logger.debug("Resource at #{url} is 404 Gone")
{:error, :http_not_found}
{:ok, %Tesla.Env{body: data}} when is_binary(data) ->
{:error, :content_not_json}
{:ok, %Tesla.Env{} = res} ->
Logger.debug("Resource returned bad HTTP code #{inspect(res)}")
{:error, :http_error}
{:error, err} ->
{:error, err}
end
end
end end

View file

@ -12,6 +12,7 @@ defmodule Mobilizon.Federation.HTTPSignatures.Signature do
alias Mobilizon.Actors.Actor alias Mobilizon.Actors.Actor
alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor
alias Mobilizon.Federation.ActivityPub.Relay
require Logger require Logger
@ -94,13 +95,19 @@ defmodule Mobilizon.Federation.HTTPSignatures.Signature do
%{"keyId" => kid} = HTTPSignatures.signature_for_conn(conn) %{"keyId" => kid} = HTTPSignatures.signature_for_conn(conn)
actor_url = key_id_to_actor_url(kid) actor_url = key_id_to_actor_url(kid)
Logger.debug("Refetching public key for #{actor_url}") Logger.debug("Refetching public key for #{actor_url}")
relay = Relay.get_actor()
# In this specific case we don't sign object fetches because if actor_url == relay.url do
# this would cause infinite recursion when servers both need # Special case if ever it's our own actor fetching ourselves
# to fetch each other's keys get_actor_public_key(relay)
with {:ok, %Actor{} = actor} <- else
ActivityPubActor.make_actor_from_url(actor_url, ignore_sign_object_fetches: true) do # In this specific case we don't sign object fetches because
get_actor_public_key(actor) # this would cause infinite recursion when servers both need
# to fetch each other's keys
with {:ok, %Actor{} = actor} <-
ActivityPubActor.make_actor_from_url(actor_url, ignore_sign_object_fetches: true) do
get_actor_public_key(actor)
end
end end
end end