[#1940] Applied rate limit for requests with bad `admin_token`. Added doc warnings on `admin_token` setting.

This commit is contained in:
Ivan Tashkinov 2020-07-14 11:58:41 +03:00
parent cf3f8cb72a
commit 9b225db7d8
4 changed files with 28 additions and 6 deletions

View File

@ -2008,13 +2008,15 @@
label: "Pleroma Admin Token", label: "Pleroma Admin Token",
type: :group, type: :group,
description: description:
"Allows to set a token that can be used to authenticate with the admin api without using an actual user by giving it as the `admin_token` parameter", "Allows to set a token that can be used to authenticate with the admin api without using an actual user by giving it as the `admin_token` parameter (risky; use HTTP Basic Auth or OAuth-based authentication if possible)",
children: [ children: [
%{ %{
key: :admin_token, key: :admin_token,
type: :string, type: :string,
description: "Admin token", description: "Admin token",
suggestions: ["We recommend a secure random string or UUID"] suggestions: [
"We recommend NOT setting the value do to increased security risk; if set, use a secure random long string or UUID (and change it as often as possible)"
]
} }
] ]
}, },

View File

@ -815,6 +815,8 @@ or
curl -H "X-Admin-Token: somerandomtoken" "http://localhost:4000/api/pleroma/admin/users/invites" curl -H "X-Admin-Token: somerandomtoken" "http://localhost:4000/api/pleroma/admin/users/invites"
``` ```
Warning: it's discouraged to use this feature because of the associated security risk: static / rarely changed instance-wide token is much weaker compared to email-password pair of a real admin user; consider using HTTP Basic Auth or OAuth-based authentication instead.
### :auth ### :auth
* `Pleroma.Web.Auth.PleromaAuthenticator`: default database authenticator. * `Pleroma.Web.Auth.PleromaAuthenticator`: default database authenticator.

View File

@ -5,15 +5,19 @@
defmodule Pleroma.Plugs.AdminSecretAuthenticationPlug do defmodule Pleroma.Plugs.AdminSecretAuthenticationPlug do
import Plug.Conn import Plug.Conn
alias Pleroma.User
alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Plugs.OAuthScopesPlug
alias Pleroma.Plugs.RateLimiter
alias Pleroma.User
def init(options) do def init(options) do
options options
end end
def secret_token do def secret_token do
Pleroma.Config.get(:admin_token) case Pleroma.Config.get(:admin_token) do
blank when blank in [nil, ""] -> nil
token -> token
end
end end
def call(%{assigns: %{user: %User{}}} = conn, _), do: conn def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
@ -30,7 +34,7 @@ def authenticate(%{params: %{"admin_token" => admin_token}} = conn) do
if admin_token == secret_token() do if admin_token == secret_token() do
assign_admin_user(conn) assign_admin_user(conn)
else else
conn handle_bad_token(conn)
end end
end end
@ -38,8 +42,9 @@ def authenticate(conn) do
token = secret_token() token = secret_token()
case get_req_header(conn, "x-admin-token") do case get_req_header(conn, "x-admin-token") do
blank when blank in [[], [""]] -> conn
[^token] -> assign_admin_user(conn) [^token] -> assign_admin_user(conn)
_ -> conn _ -> handle_bad_token(conn)
end end
end end
@ -48,4 +53,8 @@ defp assign_admin_user(conn) do
|> assign(:user, %User{is_admin: true}) |> assign(:user, %User{is_admin: true})
|> OAuthScopesPlug.skip_plug() |> OAuthScopesPlug.skip_plug()
end end
defp handle_bad_token(conn) do
RateLimiter.call(conn, name: :authentication)
end
end end

View File

@ -4,11 +4,14 @@
defmodule Pleroma.Plugs.AdminSecretAuthenticationPlugTest do defmodule Pleroma.Plugs.AdminSecretAuthenticationPlugTest do
use Pleroma.Web.ConnCase, async: true use Pleroma.Web.ConnCase, async: true
import Mock
import Pleroma.Factory import Pleroma.Factory
alias Pleroma.Plugs.AdminSecretAuthenticationPlug alias Pleroma.Plugs.AdminSecretAuthenticationPlug
alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Plugs.OAuthScopesPlug
alias Pleroma.Plugs.PlugHelper alias Pleroma.Plugs.PlugHelper
alias Pleroma.Plugs.RateLimiter
test "does nothing if a user is assigned", %{conn: conn} do test "does nothing if a user is assigned", %{conn: conn} do
user = insert(:user) user = insert(:user)
@ -27,6 +30,10 @@ test "does nothing if a user is assigned", %{conn: conn} do
describe "when secret set it assigns an admin user" do describe "when secret set it assigns an admin user" do
setup do: clear_config([:admin_token]) setup do: clear_config([:admin_token])
setup_with_mocks([{RateLimiter, [:passthrough], []}]) do
:ok
end
test "with `admin_token` query parameter", %{conn: conn} do test "with `admin_token` query parameter", %{conn: conn} do
Pleroma.Config.put(:admin_token, "password123") Pleroma.Config.put(:admin_token, "password123")
@ -35,6 +42,7 @@ test "with `admin_token` query parameter", %{conn: conn} do
|> AdminSecretAuthenticationPlug.call(%{}) |> AdminSecretAuthenticationPlug.call(%{})
refute conn.assigns[:user] refute conn.assigns[:user]
assert called(RateLimiter.call(conn, name: :authentication))
conn = conn =
%{conn | params: %{"admin_token" => "password123"}} %{conn | params: %{"admin_token" => "password123"}}
@ -53,6 +61,7 @@ test "with `x-admin-token` HTTP header", %{conn: conn} do
|> AdminSecretAuthenticationPlug.call(%{}) |> AdminSecretAuthenticationPlug.call(%{})
refute conn.assigns[:user] refute conn.assigns[:user]
assert called(RateLimiter.call(conn, name: :authentication))
conn = conn =
conn conn