Merge branch 'auth-fetch-exception' into 'develop'

HTTPSignaturePlug: Add :authorized_fetch_mode_exceptions

See merge request pleroma/pleroma!4007
This commit is contained in:
lain 2024-05-28 04:42:35 +00:00
commit 25903a4996
7 changed files with 55 additions and 17 deletions

View File

@ -0,0 +1 @@
HTTPSignaturePlug: Add :authorized_fetch_mode_exceptions configuration

View File

@ -1816,6 +1816,12 @@
type: :boolean, type: :boolean,
description: "Require HTTP signatures for AP fetches" description: "Require HTTP signatures for AP fetches"
}, },
%{
key: :authorized_fetch_mode_exceptions,
type: {:list, :string},
description:
"List of IPs (CIDR format accepted) to exempt from HTTP Signatures requirement (for example to allow debugging, you shouldn't otherwise need this)"
},
%{ %{
key: :note_replies_output_limit, key: :note_replies_output_limit,
type: :integer, type: :integer,

View File

@ -284,6 +284,7 @@ Notes:
* `deny_follow_blocked`: Whether to disallow following an account that has blocked the user in question * `deny_follow_blocked`: Whether to disallow following an account that has blocked the user in question
* `sign_object_fetches`: Sign object fetches with HTTP signatures * `sign_object_fetches`: Sign object fetches with HTTP signatures
* `authorized_fetch_mode`: Require HTTP signatures for AP fetches * `authorized_fetch_mode`: Require HTTP signatures for AP fetches
* `authorized_fetch_mode_exceptions`: List of IPs (CIDR format accepted) to exempt from HTTP Signatures requirement (for example to allow debugging, you shouldn't otherwise need this)
## Pleroma.User ## Pleroma.User

View File

@ -16,4 +16,15 @@ def parse_address(ip) when is_binary(ip) do
def parse_address(ip) do def parse_address(ip) do
:inet.parse_address(ip) :inet.parse_address(ip)
end end
def parse_cidr(proxy) when is_binary(proxy) do
proxy =
cond do
"/" in String.codepoints(proxy) -> proxy
InetCidr.v4?(InetCidr.parse_address!(proxy)) -> proxy <> "/32"
InetCidr.v6?(InetCidr.parse_address!(proxy)) -> proxy <> "/128"
end
InetCidr.parse_cidr!(proxy, true)
end
end end

View File

@ -3,6 +3,8 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
alias Pleroma.Helpers.InetHelper
import Plug.Conn import Plug.Conn
import Phoenix.Controller, only: [get_format: 1, text: 2] import Phoenix.Controller, only: [get_format: 1, text: 2]
require Logger require Logger
@ -89,12 +91,20 @@ defp has_signature_header?(conn) do
defp maybe_require_signature(%{assigns: %{valid_signature: true}} = conn), do: conn defp maybe_require_signature(%{assigns: %{valid_signature: true}} = conn), do: conn
defp maybe_require_signature(conn) do defp maybe_require_signature(%{remote_ip: remote_ip} = conn) do
if Pleroma.Config.get([:activitypub, :authorized_fetch_mode], false) do if Pleroma.Config.get([:activitypub, :authorized_fetch_mode], false) do
exceptions =
Pleroma.Config.get([:activitypub, :authorized_fetch_mode_exceptions], [])
|> Enum.map(&InetHelper.parse_cidr/1)
if Enum.any?(exceptions, fn x -> InetCidr.contains?(x, remote_ip) end) do
conn
else
conn conn
|> put_status(:unauthorized) |> put_status(:unauthorized)
|> text("Request not signed") |> text("Request not signed")
|> halt() |> halt()
end
else else
conn conn
end end

View File

@ -8,6 +8,7 @@ defmodule Pleroma.Web.Plugs.RemoteIp do
""" """
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.Helpers.InetHelper
import Plug.Conn import Plug.Conn
@behaviour Plug @behaviour Plug
@ -30,19 +31,8 @@ defp remote_ip_opts do
proxies = proxies =
Config.get([__MODULE__, :proxies], []) Config.get([__MODULE__, :proxies], [])
|> Enum.concat(reserved) |> Enum.concat(reserved)
|> Enum.map(&maybe_add_cidr/1) |> Enum.map(&InetHelper.parse_cidr/1)
{headers, proxies} {headers, proxies}
end end
defp maybe_add_cidr(proxy) when is_binary(proxy) do
proxy =
cond do
"/" in String.codepoints(proxy) -> proxy
InetCidr.v4?(InetCidr.parse_address!(proxy)) -> proxy <> "/32"
InetCidr.v6?(InetCidr.parse_address!(proxy)) -> proxy <> "/128"
end
InetCidr.parse_cidr!(proxy, true)
end
end end

View File

@ -81,5 +81,24 @@ test "halts the connection when `signature` header is not present", %{conn: conn
assert conn.state == :sent assert conn.state == :sent
assert conn.resp_body == "Request not signed" assert conn.resp_body == "Request not signed"
end end
test "exempts specific IPs from `authorized_fetch_mode_exceptions`", %{conn: conn} do
clear_config([:activitypub, :authorized_fetch_mode_exceptions], ["192.168.0.0/24"])
with_mock HTTPSignatures, validate_conn: fn _ -> false end do
conn =
conn
|> Map.put(:remote_ip, {192, 168, 0, 1})
|> put_req_header(
"signature",
"keyId=\"http://mastodon.example.org/users/admin#main-key"
)
|> HTTPSignaturePlug.call(%{})
assert conn.remote_ip == {192, 168, 0, 1}
assert conn.halted == false
assert called(HTTPSignatures.validate_conn(:_))
end
end
end end
end end