Add `remote_ip` plug
This commit is contained in:
parent
92d08d4113
commit
f9380289eb
|
@ -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: Add `/api/v1/pleroma/accounts/confirmation_resend?email=<email>` for resending account confirmation.
|
||||||
- Pleroma API: Email change endpoint.
|
- Pleroma API: Email change endpoint.
|
||||||
- Admin API: Added moderation log
|
- 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)
|
- 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`)
|
- 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.
|
- ActivityPub: Add ActivityPub actor's `discoverable` parameter.
|
||||||
|
|
|
@ -591,6 +591,8 @@
|
||||||
|
|
||||||
config :pleroma, Pleroma.ActivityExpiration, enabled: true
|
config :pleroma, Pleroma.ActivityExpiration, enabled: true
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Plugs.RemoteIp, enabled: false
|
||||||
|
|
||||||
config :pleroma, :web_cache_ttl,
|
config :pleroma, :web_cache_ttl,
|
||||||
activity_pub: nil,
|
activity_pub: nil,
|
||||||
activity_pub_question: 30_000
|
activity_pub_question: 30_000
|
||||||
|
|
|
@ -2687,6 +2687,42 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
%{
|
||||||
|
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,
|
group: :pleroma,
|
||||||
key: :web_cache_ttl,
|
key: :web_cache_ttl,
|
||||||
|
|
|
@ -730,6 +730,8 @@ This will probably take a long time.
|
||||||
|
|
||||||
This is an advanced feature and disabled by default.
|
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:
|
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.
|
* 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` - 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).
|
* `: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 Upgrade $http_upgrade;
|
||||||
proxy_set_header Connection "upgrade";
|
proxy_set_header Connection "upgrade";
|
||||||
proxy_set_header Host $http_host;
|
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
|
# this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only
|
||||||
# and `localhost.` resolves to [::0] on some systems: see issue #930
|
# 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
|
extra: extra
|
||||||
)
|
)
|
||||||
|
|
||||||
# Note: the plug and its configuration is compile-time this can't be upstreamed yet
|
plug(Pleroma.Plugs.RemoteIp)
|
||||||
if proxies = Pleroma.Config.get([__MODULE__, :reverse_proxies]) do
|
|
||||||
plug(RemoteIp, proxies: proxies)
|
|
||||||
end
|
|
||||||
|
|
||||||
defmodule Instrumenter do
|
defmodule Instrumenter do
|
||||||
use Prometheus.PhoenixInstrumenter
|
use Prometheus.PhoenixInstrumenter
|
||||||
|
|
3
mix.exs
3
mix.exs
|
@ -159,6 +159,9 @@ defp deps do
|
||||||
{:plug_static_index_html, "~> 1.0.0"},
|
{:plug_static_index_html, "~> 1.0.0"},
|
||||||
{:excoveralls, "~> 0.11.1", only: :test},
|
{:excoveralls, "~> 0.11.1", only: :test},
|
||||||
{:flake_id, "~> 0.1.0"},
|
{:flake_id, "~> 0.1.0"},
|
||||||
|
{:remote_ip,
|
||||||
|
git: "https://git.pleroma.social/pleroma/remote_ip.git",
|
||||||
|
ref: "825dc00aaba5a1b7c4202a532b696b595dd3bcb3"},
|
||||||
{:mox, "~> 0.5", only: :test}
|
{:mox, "~> 0.5", only: :test}
|
||||||
] ++ oauth_deps()
|
] ++ oauth_deps()
|
||||||
end
|
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"]},
|
"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"},
|
"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"},
|
"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"},
|
"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"},
|
"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"},
|
"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"},
|
"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"},
|
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
|
||||||
"recon": {:git, "https://github.com/ferd/recon.git", "75d70c7c08926d2f24f1ee6de14ee50fe8a52763", [tag: "2.4.0"]},
|
"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"},
|
"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"},
|
"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"},
|
"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
|
Loading…
Reference in New Issue