Merge branch 'remote-ip' into 'develop'
Add `remote_ip` plug See merge request pleroma/pleroma!1608
This commit is contained in:
commit
705ea1b975
@ -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
|
||||
|
@ -2687,6 +2687,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,
|
||||
|
@ -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
|
||||
|
54
lib/pleroma/plugs/remote_ip.ex
Normal file
54
lib/pleroma/plugs/remote_ip.ex
Normal file
@ -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
|
||||
|
3
mix.exs
3
mix.exs
@ -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
|
||||
|
2
mix.lock
2
mix.lock
@ -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"},
|
||||
|
72
test/plugs/remote_ip_test.exs
Normal file
72
test/plugs/remote_ip_test.exs
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user