@@ -113,6 +113,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). | |||
- Pleroma API: Add `/api/v1/pleroma/accounts/confirmation_resend?email=<email>` for resending account confirmation. | |||
- Pleroma API: Email change endpoint. | |||
- Admin API: Added moderation log | |||
- Support for `X-Forwarded-For` and similar HTTP headers which used by reverse proxies to pass a real user IP address to the backend. Must not be enabled unless your instance is behind at least one reverse proxy (such as Nginx, Apache HTTPD or Varnish Cache). | |||
- Web response cache (currently, enabled for ActivityPub) | |||
- Mastodon API: Added an endpoint to get multiple statuses by IDs (`GET /api/v1/statuses/?ids[]=1&ids[]=2`) | |||
- ActivityPub: Add ActivityPub actor's `discoverable` parameter. | |||
@@ -591,6 +591,8 @@ config :pleroma, :rate_limit, nil | |||
config :pleroma, Pleroma.ActivityExpiration, enabled: true | |||
config :pleroma, Pleroma.Plugs.RemoteIp, enabled: false | |||
config :pleroma, :web_cache_ttl, | |||
activity_pub: nil, | |||
activity_pub_question: 30_000 | |||
@@ -2689,6 +2689,42 @@ config :pleroma, :config_description, [ | |||
}, | |||
%{ | |||
group: :pleroma, | |||
key: Pleroma.Plugs.RemoteIp, | |||
type: :group, | |||
description: """ | |||
**If your instance is not behind at least one reverse proxy, you should not enable this plug.** | |||
`Pleroma.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration. | |||
""", | |||
children: [ | |||
%{ | |||
key: :enabled, | |||
type: :boolean, | |||
description: "Enable/disable the plug. Defaults to `false`.", | |||
suggestions: [true, false] | |||
}, | |||
%{ | |||
key: :headers, | |||
type: {:list, :string}, | |||
description: | |||
"A list of strings naming the `req_headers` to use when deriving the `remote_ip`. Order does not matter. Defaults to `~w[forwarded x-forwarded-for x-client-ip x-real-ip]`." | |||
}, | |||
%{ | |||
key: :proxies, | |||
type: {:list, :string}, | |||
description: | |||
"A list of strings in [CIDR](https://en.wikipedia.org/wiki/CIDR) notation specifying the IPs of known proxies. Defaults to `[]`." | |||
}, | |||
%{ | |||
key: :reserved, | |||
type: {:list, :string}, | |||
description: | |||
"Defaults to [localhost](https://en.wikipedia.org/wiki/Localhost) and [private network](https://en.wikipedia.org/wiki/Private_network)." | |||
} | |||
] | |||
}, | |||
%{ | |||
group: :pleroma, | |||
key: :web_cache_ttl, | |||
type: :group, | |||
description: | |||
@@ -730,6 +730,8 @@ This will probably take a long time. | |||
This is an advanced feature and disabled by default. | |||
If your instance is behind a reverse proxy you must enable and configure [`Pleroma.Plugs.RemoteIp`](#pleroma-plugs-remoteip). | |||
A keyword list of rate limiters where a key is a limiter name and value is the limiter configuration. The basic configuration is a tuple where: | |||
* The first element: `scale` (Integer). The time scale in milliseconds. | |||
@@ -756,3 +758,16 @@ Available caches: | |||
* `:activity_pub` - activity pub routes (except question activities). Defaults to `nil` (no expiration). | |||
* `:activity_pub_question` - activity pub routes (question activities). Defaults to `30_000` (30 seconds). | |||
## Pleroma.Plugs.RemoteIp | |||
**If your instance is not behind at least one reverse proxy, you should not enable this plug.** | |||
`Pleroma.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration. | |||
Available options: | |||
* `enabled` - Enable/disable the plug. Defaults to `false`. | |||
* `headers` - A list of strings naming the `req_headers` to use when deriving the `remote_ip`. Order does not matter. Defaults to `~w[forwarded x-forwarded-for x-client-ip x-real-ip]`. | |||
* `proxies` - A list of strings in [CIDR](https://en.wikipedia.org/wiki/CIDR) notation specifying the IPs of known proxies. Defaults to `[]`. | |||
* `reserved` - Defaults to [localhost](https://en.wikipedia.org/wiki/Localhost) and [private network](https://en.wikipedia.org/wiki/Private_network). |
@@ -70,6 +70,7 @@ server { | |||
proxy_set_header Upgrade $http_upgrade; | |||
proxy_set_header Connection "upgrade"; | |||
proxy_set_header Host $http_host; | |||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |||
# this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only | |||
# and `localhost.` resolves to [::0] on some systems: see issue #930 | |||
@@ -0,0 +1,54 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Plugs.RemoteIp do | |||
@moduledoc """ | |||
This is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration. | |||
""" | |||
@behaviour Plug | |||
@headers ~w[ | |||
forwarded | |||
x-forwarded-for | |||
x-client-ip | |||
x-real-ip | |||
] | |||
# https://en.wikipedia.org/wiki/Localhost | |||
# https://en.wikipedia.org/wiki/Private_network | |||
@reserved ~w[ | |||
127.0.0.0/8 | |||
::1/128 | |||
fc00::/7 | |||
10.0.0.0/8 | |||
172.16.0.0/12 | |||
192.168.0.0/16 | |||
] | |||
def init(_), do: nil | |||
def call(conn, _) do | |||
config = Pleroma.Config.get(__MODULE__, []) | |||
if Keyword.get(config, :enabled, false) do | |||
RemoteIp.call(conn, remote_ip_opts(config)) | |||
else | |||
conn | |||
end | |||
end | |||
defp remote_ip_opts(config) do | |||
headers = config |> Keyword.get(:headers, @headers) |> MapSet.new() | |||
reserved = Keyword.get(config, :reserved, @reserved) | |||
proxies = | |||
config | |||
|> Keyword.get(:proxies, []) | |||
|> Enum.concat(reserved) | |||
|> Enum.map(&InetCidr.parse/1) | |||
{headers, proxies} | |||
end | |||
end |
@@ -97,10 +97,7 @@ defmodule Pleroma.Web.Endpoint do | |||
extra: extra | |||
) | |||
# Note: the plug and its configuration is compile-time this can't be upstreamed yet | |||
if proxies = Pleroma.Config.get([__MODULE__, :reverse_proxies]) do | |||
plug(RemoteIp, proxies: proxies) | |||
end | |||
plug(Pleroma.Plugs.RemoteIp) | |||
defmodule Instrumenter do | |||
use Prometheus.PhoenixInstrumenter | |||
@@ -159,6 +159,9 @@ defmodule Pleroma.Mixfile do | |||
{:plug_static_index_html, "~> 1.0.0"}, | |||
{:excoveralls, "~> 0.11.1", only: :test}, | |||
{:flake_id, "~> 0.1.0"}, | |||
{:remote_ip, | |||
git: "https://git.pleroma.social/pleroma/remote_ip.git", | |||
ref: "825dc00aaba5a1b7c4202a532b696b595dd3bcb3"}, | |||
{:mox, "~> 0.5", only: :test} | |||
] ++ oauth_deps() | |||
end | |||
@@ -48,6 +48,7 @@ | |||
"http_signatures": {:git, "https://git.pleroma.social/pleroma/http_signatures.git", "293d77bb6f4a67ac8bde1428735c3b42f22cbb30", [ref: "293d77bb6f4a67ac8bde1428735c3b42f22cbb30"]}, | |||
"httpoison": {:hex, :httpoison, "1.2.0", "2702ed3da5fd7a8130fc34b11965c8cfa21ade2f232c00b42d96d4967c39a3a3", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, | |||
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, | |||
"inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm"}, | |||
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, | |||
"joken": {:hex, :joken, "2.0.1", "ec9ab31bf660f343380da033b3316855197c8d4c6ef597fa3fcb451b326beb14", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm"}, | |||
"jose": {:hex, :jose, "1.9.0", "4167c5f6d06ffaebffd15cdb8da61a108445ef5e85ab8f5a7ad926fdf3ada154", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"}, | |||
@@ -87,6 +88,7 @@ | |||
"quantum": {:hex, :quantum, "2.3.4", "72a0e8855e2adc101459eac8454787cb74ab4169de6ca50f670e72142d4960e9", [:mix], [{:calendar, "~> 0.17", [hex: :calendar, repo: "hexpm", optional: true]}, {:crontab, "~> 1.1", [hex: :crontab, repo: "hexpm", optional: false]}, {:gen_stage, "~> 0.12", [hex: :gen_stage, repo: "hexpm", optional: false]}, {:swarm, "~> 3.3", [hex: :swarm, repo: "hexpm", optional: false]}, {:timex, "~> 3.1", [hex: :timex, repo: "hexpm", optional: true]}], "hexpm"}, | |||
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"}, | |||
"recon": {:git, "https://github.com/ferd/recon.git", "75d70c7c08926d2f24f1ee6de14ee50fe8a52763", [tag: "2.4.0"]}, | |||
"remote_ip": {:git, "https://git.pleroma.social/pleroma/remote_ip.git", "825dc00aaba5a1b7c4202a532b696b595dd3bcb3", [ref: "825dc00aaba5a1b7c4202a532b696b595dd3bcb3"]}, | |||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm"}, | |||
"swarm": {:hex, :swarm, "3.4.0", "64f8b30055d74640d2186c66354b33b999438692a91be275bb89cdc7e401f448", [:mix], [{:gen_state_machine, "~> 2.0", [hex: :gen_state_machine, repo: "hexpm", optional: false]}, {:libring, "~> 1.0", [hex: :libring, repo: "hexpm", optional: false]}], "hexpm"}, | |||
"sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm"}, | |||
@@ -0,0 +1,72 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Plugs.RemoteIpTest do | |||
use ExUnit.Case, async: true | |||
use Plug.Test | |||
alias Pleroma.Plugs.RemoteIp | |||
test "disabled" do | |||
Pleroma.Config.put(RemoteIp, enabled: false) | |||
%{remote_ip: remote_ip} = conn(:get, "/") | |||
conn = | |||
conn(:get, "/") | |||
|> put_req_header("x-forwarded-for", "1.1.1.1") | |||
|> RemoteIp.call(nil) | |||
assert conn.remote_ip == remote_ip | |||
end | |||
test "enabled" do | |||
Pleroma.Config.put(RemoteIp, enabled: true) | |||
conn = | |||
conn(:get, "/") | |||
|> put_req_header("x-forwarded-for", "1.1.1.1") | |||
|> RemoteIp.call(nil) | |||
assert conn.remote_ip == {1, 1, 1, 1} | |||
end | |||
test "custom headers" do | |||
Pleroma.Config.put(RemoteIp, enabled: true, headers: ["cf-connecting-ip"]) | |||
conn = | |||
conn(:get, "/") | |||
|> put_req_header("x-forwarded-for", "1.1.1.1") | |||
|> RemoteIp.call(nil) | |||
refute conn.remote_ip == {1, 1, 1, 1} | |||
conn = | |||
conn(:get, "/") | |||
|> put_req_header("cf-connecting-ip", "1.1.1.1") | |||
|> RemoteIp.call(nil) | |||
assert conn.remote_ip == {1, 1, 1, 1} | |||
end | |||
test "custom proxies" do | |||
Pleroma.Config.put(RemoteIp, enabled: true) | |||
conn = | |||
conn(:get, "/") | |||
|> put_req_header("x-forwarded-for", "173.245.48.1, 1.1.1.1, 173.245.48.2") | |||
|> RemoteIp.call(nil) | |||
refute conn.remote_ip == {1, 1, 1, 1} | |||
Pleroma.Config.put([RemoteIp, :proxies], ["173.245.48.0/20"]) | |||
conn = | |||
conn(:get, "/") | |||
|> put_req_header("x-forwarded-for", "173.245.48.1, 1.1.1.1, 173.245.48.2") | |||
|> RemoteIp.call(nil) | |||
assert conn.remote_ip == {1, 1, 1, 1} | |||
end | |||
end |