Added tests and documentation.
- Media preview proxy (requires `ffmpeg` and `ImageMagick` to be installed and media proxy to be enabled; see `:media_preview_proxy` config for more details).
- Pleroma API: Importing the mutes users from CSV files.
- Experimental websocket-based federation between Pleroma instances.
+- App metrics: ability to restrict access to specified IP whitelist.
### Changed
- **Breaking** Requires `libmagic` (or `file`) to guess file types.
- **Breaking:** Pleroma Admin API: emoji packs and files routes changed.
- **Breaking:** Sensitive/NSFW statuses no longer disable link previews.
+- **Breaking:** App metrics endpoint (`/api/pleroma/app_metrics`) is disabled by default, check `docs/API/prometheus.md` on enabling and configuring.
- Search: Users are now findable by their urls.
- Renamed `:await_up_timeout` in `:connections_pool` namespace to `:connect_timeout`, old name is deprecated.
- Renamed `:timeout` in `pools` namespace to `:recv_timeout`, old name is deprecated.
config :pleroma, Pleroma.Emails.NewUsersDigestEmail, enabled: false
-config :prometheus, Pleroma.Web.Endpoint.MetricsExporter, path: "/api/pleroma/app_metrics"
+config :prometheus, Pleroma.Web.Endpoint.MetricsExporter,
+ enabled: false,
+ auth: false,
+ ip_whitelist: [],
+ path: "/api/pleroma/app_metrics",
+ format: :text
config :pleroma, Pleroma.ScheduledActivity,
daily_user_limit: 25,
Pleroma includes support for exporting metrics via the [prometheus_ex](https://github.com/deadtrickster/prometheus.ex) library.
+Config example:
+
+```
+config :prometheus, Pleroma.Web.Endpoint.MetricsExporter,
+ enabled: true,
+ auth: {:basic, "myusername", "mypassword"},
+ ip_whitelist: ["127.0.0.1"],
+ path: "/api/pleroma/app_metrics",
+ format: :text
+```
+
+* `enabled` (Pleroma extension) enables the endpoint
+* `ip_whitelist` (Pleroma extension) could be used to restrict access only to specified IPs
+* `auth` sets the authentication (`false` for no auth; configurable to HTTP Basic Auth, see [prometheus-plugs](https://github.com/deadtrickster/prometheus-plugs#exporting) documentation)
+* `format` sets the output format (`:text` or `:protobuf`)
+* `path` sets the path to app metrics page
+
+
## `/api/pleroma/app_metrics`
+
### Exports Prometheus application metrics
+
* Method: `GET`
-* Authentication: not required
+* Authentication: not required by default (see configuration options above)
* Params: none
-* Response: JSON
+* Response: text
## Grafana
+
### Config example
+
The following is a config example to use with [Grafana](https://grafana.com)
```
--- /dev/null
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Helpers.InetHelper do
+ def parse_address(ip) when is_tuple(ip) do
+ {:ok, ip}
+ end
+
+ def parse_address(ip) when is_binary(ip) do
+ ip
+ |> String.to_charlist()
+ |> parse_address()
+ end
+
+ def parse_address(ip) do
+ :inet.parse_address(ip)
+ end
+end
require Pleroma.Constants
+ alias Pleroma.Config
+
socket("/socket", Pleroma.Web.UserSocket)
plug(Pleroma.Web.Plugs.SetLocalePlug)
plug(Plug.Parsers,
parsers: [
:urlencoded,
- {:multipart, length: {Pleroma.Config, :get, [[:instance, :upload_limit]]}},
+ {:multipart, length: {Config, :get, [[:instance, :upload_limit]]}},
:json
],
pass: ["*/*"],
json_decoder: Jason,
- length: Pleroma.Config.get([:instance, :upload_limit]),
+ length: Config.get([:instance, :upload_limit]),
body_reader: {Pleroma.Web.Plugs.DigestPlug, :read_body, []}
)
plug(Plug.MethodOverride)
plug(Plug.Head)
- secure_cookies = Pleroma.Config.get([__MODULE__, :secure_cookie_flag])
+ secure_cookies = Config.get([__MODULE__, :secure_cookie_flag])
cookie_name =
if secure_cookies,
else: "pleroma_key"
extra =
- Pleroma.Config.get([__MODULE__, :extra_cookie_attrs])
+ Config.get([__MODULE__, :extra_cookie_attrs])
|> Enum.join(";")
# The session will be stored in the cookie and signed,
Plug.Session,
store: :cookie,
key: cookie_name,
- signing_salt: Pleroma.Config.get([__MODULE__, :signing_salt], "CqaoopA2"),
+ signing_salt: Config.get([__MODULE__, :signing_salt], "CqaoopA2"),
http_only: true,
secure: secure_cookies,
extra: extra
use Prometheus.PlugExporter
end
+ defmodule MetricsExporterCaller do
+ @behaviour Plug
+
+ def init(opts), do: opts
+
+ def call(conn, opts) do
+ prometheus_config = Application.get_env(:prometheus, MetricsExporter, [])
+ ip_whitelist = List.wrap(prometheus_config[:ip_whitelist])
+
+ cond do
+ !prometheus_config[:enabled] ->
+ conn
+
+ ip_whitelist != [] and
+ !Enum.find(ip_whitelist, fn ip ->
+ Pleroma.Helpers.InetHelper.parse_address(ip) == {:ok, conn.remote_ip}
+ end) ->
+ conn
+
+ true ->
+ MetricsExporter.call(conn, opts)
+ end
+ end
+ end
+
plug(PipelineInstrumenter)
- plug(MetricsExporter)
+
+ plug(MetricsExporterCaller)
plug(Pleroma.Web.Router)
--- /dev/null
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Endpoint.MetricsExporterTest do
+ use Pleroma.Web.ConnCase
+
+ alias Pleroma.Web.Endpoint.MetricsExporter
+
+ defp config do
+ Application.get_env(:prometheus, MetricsExporter)
+ end
+
+ describe "with default config" do
+ test "does NOT expose app metrics", %{conn: conn} do
+ conn
+ |> get(config()[:path])
+ |> json_response(404)
+ end
+ end
+
+ describe "when enabled" do
+ setup do
+ initial_config = config()
+ on_exit(fn -> Application.put_env(:prometheus, MetricsExporter, initial_config) end)
+
+ Application.put_env(
+ :prometheus,
+ MetricsExporter,
+ Keyword.put(initial_config, :enabled, true)
+ )
+ end
+
+ test "serves app metrics", %{conn: conn} do
+ conn = get(conn, config()[:path])
+ assert response = response(conn, 200)
+
+ for metric <- [
+ "http_requests_total",
+ "http_request_duration_microseconds",
+ "phoenix_controller_render_duration",
+ "phoenix_controller_call_duration",
+ "telemetry_scrape_duration",
+ "erlang_vm_memory_atom_bytes_total"
+ ] do
+ assert response =~ ~r/#{metric}/
+ end
+ end
+
+ test "when IP whitelist configured, " <>
+ "serves app metrics only if client IP is whitelisted",
+ %{conn: conn} do
+ Application.put_env(
+ :prometheus,
+ MetricsExporter,
+ Keyword.put(config(), :ip_whitelist, ["127.127.127.127", {1, 1, 1, 1}, '255.255.255.255'])
+ )
+
+ conn
+ |> get(config()[:path])
+ |> json_response(404)
+
+ conn
+ |> Map.put(:remote_ip, {127, 127, 127, 127})
+ |> get(config()[:path])
+ |> response(200)
+ end
+ end
+end