Merge pull request 'Federation: do not deliver to instances rejected by SimplePolicy' (#8021) from lambadalambda/pleroma:fix/federation-reject-instances into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/8021
This commit is contained in:
commit
c2bdfb6a34
11 changed files with 243 additions and 35 deletions
|
|
@ -164,6 +164,39 @@ defmodule Pleroma.Web.ActivityPub.MRF do
|
|||
Enum.any?(domains, fn domain -> Regex.match?(domain, host) end)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a function that is false for a host if SimplePolicy is enabled and
|
||||
rejects it, or has an accept list without it. Used for deliveries and signed
|
||||
fetches, not only for incoming activities. Build it once and reuse it: it
|
||||
compiles the instance lists.
|
||||
"""
|
||||
@spec instance_filter() :: (String.t() | nil -> boolean())
|
||||
def instance_filter do
|
||||
if Pleroma.Web.ActivityPub.MRF.SimplePolicy in get_policies() do
|
||||
reject = simple_policy_domains(:reject)
|
||||
accept = simple_policy_domains(:accept)
|
||||
|
||||
fn
|
||||
host when is_binary(host) ->
|
||||
not subdomain_match?(reject, host) and (accept == [] or subdomain_match?(accept, host))
|
||||
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
else
|
||||
&is_binary/1
|
||||
end
|
||||
end
|
||||
|
||||
@spec instance_allowed?(String.t() | nil) :: boolean()
|
||||
def instance_allowed?(host), do: instance_filter().(host)
|
||||
|
||||
defp simple_policy_domains(key) do
|
||||
Pleroma.Config.get([:mrf_simple, key], [])
|
||||
|> instance_list_from_tuples()
|
||||
|> subdomains_regex()
|
||||
end
|
||||
|
||||
@spec instance_list_from_tuples([{String.t(), String.t()}]) :: [String.t()]
|
||||
def instance_list_from_tuples(list) do
|
||||
Enum.map(list, fn {instance, _} -> instance end)
|
||||
|
|
|
|||
|
|
@ -333,12 +333,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
|||
%{
|
||||
key: :reject,
|
||||
description:
|
||||
"List of instances to reject activities from (except deletes) and the reason for doing so"
|
||||
"List of instances to reject activities from (except deletes) and the reason for doing so. We also do not deliver to them, and refuse their signed fetches in authorized fetch mode"
|
||||
},
|
||||
%{
|
||||
key: :accept,
|
||||
description:
|
||||
"List of instances to only accept activities from (except deletes) and the reason for doing so"
|
||||
"List of instances to only accept activities from (except deletes) and the reason for doing so. If set, we also only deliver to them, and refuse signed fetches from others in authorized fetch mode"
|
||||
},
|
||||
%{
|
||||
key: :followers_only,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.MRF
|
||||
alias Pleroma.Web.ActivityPub.Publisher.Prepared
|
||||
alias Pleroma.Web.ActivityPub.Relay
|
||||
alias Pleroma.Workers.PublisherWorker
|
||||
|
|
@ -221,18 +222,25 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
end
|
||||
end
|
||||
|
||||
def should_federate?(nil, _), do: false
|
||||
def should_federate?(_, true), do: true
|
||||
def should_federate?(inbox, public), do: federation_filter(public).(inbox)
|
||||
|
||||
def should_federate?(inbox, _) do
|
||||
%{host: host} = URI.parse(inbox)
|
||||
# Compiles the instance lists once, for filtering many inboxes.
|
||||
defp federation_filter(public) do
|
||||
allowed? = MRF.instance_filter()
|
||||
|
||||
quarantined_instances =
|
||||
quarantined =
|
||||
Config.get([:instance, :quarantined_instances], [])
|
||||
|> Pleroma.Web.ActivityPub.MRF.instance_list_from_tuples()
|
||||
|> Pleroma.Web.ActivityPub.MRF.subdomains_regex()
|
||||
|> MRF.instance_list_from_tuples()
|
||||
|> MRF.subdomains_regex()
|
||||
|
||||
!Pleroma.Web.ActivityPub.MRF.subdomain_match?(quarantined_instances, host)
|
||||
fn
|
||||
nil ->
|
||||
false
|
||||
|
||||
inbox ->
|
||||
%{host: host} = URI.parse(inbox)
|
||||
allowed?.(host) and (public or not MRF.subdomain_match?(quarantined, host))
|
||||
end
|
||||
end
|
||||
|
||||
@spec recipients(User.t(), Activity.t()) :: [[User.t()]]
|
||||
|
|
@ -312,7 +320,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
|
||||
def publish(%User{} = actor, %{data: %{"bcc" => bcc}} = activity)
|
||||
when is_list(bcc) and bcc != [] do
|
||||
public = public?(activity)
|
||||
should_federate? = activity |> public?() |> federation_filter()
|
||||
|
||||
[priority_recipients, recipients] = recipients(actor, activity)
|
||||
|
||||
|
|
@ -324,7 +332,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
determine_inbox(activity, user)
|
||||
end)
|
||||
|> Enum.uniq()
|
||||
|> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
|
||||
|> Enum.filter(should_federate?)
|
||||
|> Instances.filter_reachable()
|
||||
end)
|
||||
|
||||
|
|
@ -360,6 +368,8 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
Relay.publish(activity)
|
||||
end
|
||||
|
||||
should_federate? = federation_filter(public)
|
||||
|
||||
[priority_inboxes, inboxes] =
|
||||
recipients(actor, activity)
|
||||
|> Enum.map(fn recipients ->
|
||||
|
|
@ -368,7 +378,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
determine_inbox(activity, user)
|
||||
end)
|
||||
|> Enum.uniq()
|
||||
|> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
|
||||
|> Enum.filter(should_federate?)
|
||||
end)
|
||||
|
||||
inboxes = inboxes -- priority_inboxes
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
|
|||
conn.assigns[:actor_id] do
|
||||
%{host: host} = URI.parse(conn.assigns.actor_id)
|
||||
|
||||
if MRF.subdomain_match?(rejected_domains(), host) do
|
||||
if MRF.subdomain_match?(rejected_domains(), host) or not MRF.instance_allowed?(host) do
|
||||
conn
|
||||
|> put_status(:unauthorized)
|
||||
|> halt()
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ defmodule Pleroma.Workers.PublisherWorker do
|
|||
alias Pleroma.Activity
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Instances
|
||||
alias Pleroma.Web.ActivityPub.Publisher
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.Federator
|
||||
|
||||
use Oban.Worker, queue: :federator_outgoing, max_attempts: 13
|
||||
|
|
@ -32,26 +34,39 @@ defmodule Pleroma.Workers.PublisherWorker do
|
|||
end
|
||||
end)
|
||||
|
||||
# Cancel / skip the job if this server believed to be unreachable now
|
||||
if not Instances.reachable?(params.inbox) do
|
||||
{:cancel, :unreachable}
|
||||
else
|
||||
case federator().perform(:publish_one, params) do
|
||||
{:ok, _} ->
|
||||
:ok
|
||||
cond do
|
||||
# Cancel / skip the job if this server believed to be unreachable now
|
||||
not Instances.reachable?(params.inbox) ->
|
||||
{:cancel, :unreachable}
|
||||
|
||||
{:error, _} = error ->
|
||||
# Only mark as unreachable on final failure
|
||||
if job.attempt == job.max_attempts do
|
||||
Instances.set_unreachable(params.inbox)
|
||||
end
|
||||
# The instance may have been blocked after the job was queued
|
||||
not federate?(params) ->
|
||||
{:cancel, :rejected}
|
||||
|
||||
error
|
||||
true ->
|
||||
case federator().perform(:publish_one, params) do
|
||||
{:ok, _} ->
|
||||
:ok
|
||||
|
||||
error ->
|
||||
# Unexpected error, may have been client side
|
||||
error
|
||||
end
|
||||
{:error, _} = error ->
|
||||
# Only mark as unreachable on final failure
|
||||
if job.attempt == job.max_attempts do
|
||||
Instances.set_unreachable(params.inbox)
|
||||
end
|
||||
|
||||
error
|
||||
|
||||
error ->
|
||||
# Unexpected error, may have been client side
|
||||
error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp federate?(%{inbox: inbox, activity_id: activity_id}) do
|
||||
case Activity.get_by_id(activity_id) do
|
||||
%Activity{} = activity -> Publisher.should_federate?(inbox, Visibility.public?(activity))
|
||||
nil -> true
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue