diff --git a/config/description.exs b/config/description.exs index 93df0bd42..030ce276f 100644 --- a/config/description.exs +++ b/config/description.exs @@ -423,6 +423,7 @@ label: "URI Schemes", type: :group, description: "URI schemes related settings", + db_exclusion_reasons: "Does not make sense to configure dynamically", children: [ %{ key: :valid_schemes, @@ -1638,6 +1639,7 @@ key: Pleroma.Web.MediaProxy.Invalidation.Script, type: :group, description: "Invalidation script settings", + db_exclusion_reason: "Provides an arbitrary execution path", children: [ %{ key: :script_path, @@ -1751,6 +1753,7 @@ %{ group: :web_push_encryption, key: :vapid_details, + db_exclusion_reason: "Webserver secret keys", label: "Vapid Details", type: :group, description: @@ -1822,19 +1825,13 @@ %{ group: :pleroma, label: "Pleroma Admin Token", - type: :group, description: "Allows setting a token that can be used to authenticate requests with admin privileges without a normal user account token. Append the `admin_token` parameter to requests to utilize it. (Please reconsider using HTTP Basic Auth or OAuth-based authentication if possible)", - children: [ - %{ - key: :admin_token, - type: :string, - description: "Admin token", - suggestions: [ - "Please use a high entropy string or UUID" - ] - } - ] + type: :string, + suggestions: [ + "Please use a high entropy string or UUID" + ], + db_exclusion_reason: "Can provide passwordless admin access" }, %{ group: :pleroma, @@ -2178,6 +2175,7 @@ label: "Pleroma Authenticator", type: :group, description: "Authenticator", + db_exclusion_reason: "Should be provided at boot-time", children: [ %{ key: Pleroma.Web.Auth.Authenticator, @@ -2191,6 +2189,7 @@ key: :ldap, label: "LDAP", type: :group, + db_exclusion_reason: "Provides access to another service", description: "Use LDAP for user authentication. When a user logs in to the Pleroma instance, the name and password" <> " will be verified by trying to authenticate (bind) to a LDAP server." <> @@ -2590,6 +2589,7 @@ label: "Mime Types", type: :group, description: "Mime Types settings", + db_exclusion_reason: "Should be provided at compile-time", children: [ %{ key: :types, @@ -2796,6 +2796,7 @@ group: :cors_plug, label: "CORS plug config", type: :group, + db_exclusion_reason: "Should be provided at compile-time", children: [ %{ key: :max_age, @@ -2953,6 +2954,7 @@ key: :modules, type: :group, description: "Custom Runtime Modules", + db_exclusion_reason: "Allows for custom elixir execution", children: [ %{ key: :runtime_dir, @@ -3089,6 +3091,7 @@ group: :ex_aws, key: :s3, type: :group, + db_exclusion_reason: "Provides access to another service", descriptions: "S3 service related settings", children: [ %{ @@ -3468,6 +3471,7 @@ key: :argos_translate, type: :group, description: "ArgosTranslate Settings.", + db_exclusion_reason: "Excluded for being able to set arbitrary paths to executables", children: [ %{ key: :command_argos_translate, diff --git a/lib/mix/tasks/pleroma/config.ex b/lib/mix/tasks/pleroma/config.ex index 8661d8d7c..98d9df2c0 100644 --- a/lib/mix/tasks/pleroma/config.ex +++ b/lib/mix/tasks/pleroma/config.ex @@ -10,6 +10,7 @@ defmodule Mix.Tasks.Pleroma.Config do alias Pleroma.ConfigDB alias Pleroma.Repo + alias Pleroma.Config.ConfigurableFromDatabase @shortdoc "Manages the location of the config" @moduledoc File.read!("docs/docs/administration/CLI_tasks/config.md") @@ -244,6 +245,28 @@ def run(["delete", group]) do end end + # Primarily a developer tool to check nothing was missed from + # db configwhitelist + def run(["check-allowed"]) do + start_pleroma() + Pleroma.Docs.JSON.compile() + raw = Pleroma.Docs.JSON.compiled_descriptions() + whitelisted = Enum.filter(raw, &ConfigurableFromDatabase.whitelisted_config?/1) + raw_map = MapSet.new(raw) + whitelisted_map = MapSet.new(whitelisted) + IO.puts("Config keys defined in description.exs but not listed as explicitly allowed in the db") + IO.puts(" Please check that standard admins should not need to touch the listed settings whilst the server is live.") + IO.puts(" !! Please remember that admins are not neccesarily sysadmins nor are they immune to oauth/password leakage.") + IO.puts("-------------") + + MapSet.difference(raw_map, whitelisted_map) + |> Enum.each(fn map -> + IO.puts("#{map[:group]}, #{map[:key]} (#{map[:label]})") + IO.puts(map[:db_exclusion_reason] || "No exclusion reason set") + IO.puts("++") + end) + end + @spec migrate_to_db(Path.t() | nil) :: any() def migrate_to_db(file_path \\ nil) do with :ok <- Pleroma.Config.DeprecationWarnings.warn() do diff --git a/lib/pleroma/config/configurable_from_database.ex b/lib/pleroma/config/configurable_from_database.ex index bbc37f605..6913325d9 100644 --- a/lib/pleroma/config/configurable_from_database.ex +++ b/lib/pleroma/config/configurable_from_database.ex @@ -1,4 +1,6 @@ defmodule Pleroma.Config.ConfigurableFromDatabase do + alias Pleroma.Config + # Basically it's silly to let this be configurable # set a list of things that we can set in the database # this is mostly our stuff, with some extra in there @@ -9,6 +11,7 @@ defmodule Pleroma.Config.ConfigurableFromDatabase do {:pleroma, Pleroma.Upload}, {:pleroma, Pleroma.Uploaders.Local}, {:pleroma, Pleroma.Uploaders.S3}, + {:pleroma, :auth}, {:pleroma, :emoji}, {:pleroma, :http}, {:pleroma, :instance}, @@ -70,9 +73,34 @@ defmodule Pleroma.Config.ConfigurableFromDatabase do {:pleroma, Pleroma.Search.Elasticsearch.Cluster}, {:pleroma, :translator}, {:pleroma, :deepl}, - {:pleroma, :libre_translate} + {:pleroma, :libre_translate}, # But not argostranslate, because executables! + {:pleroma, Pleroma.Upload.Filter.AnonymizeFilename}, + {:pleroma, Pleroma.Upload.Filter.Mogrify}, + {:pleroma, Pleroma.Workers.PurgeExpiredActivity}, + {:pleroma, :rate_limit} ] def allowed_groups, do: @allowed_groups + + def enabled, do: Config.get(:configurable_from_database) + + def whitelisted_config?(group, key) do + allowed_groups() + |> Enum.any?(fn + {whitelisted_group} -> + group == inspect(whitelisted_group) + + {whitelisted_group, whitelisted_key} -> + group == inspect(whitelisted_group) && key == inspect(whitelisted_key) + end) + end + + def whitelisted_config?(%{group: group, key: key}) do + whitelisted_config?(group, key) + end + + def whitelisted_config?(%{group: group} = config) do + whitelisted_config?(group, config[:key]) + end end diff --git a/lib/pleroma/web/admin_api/controllers/config_controller.ex b/lib/pleroma/web/admin_api/controllers/config_controller.ex index 17470fe1d..8b2401ef4 100644 --- a/lib/pleroma/web/admin_api/controllers/config_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/config_controller.ex @@ -8,6 +8,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do alias Pleroma.Config alias Pleroma.ConfigDB alias Pleroma.Web.Plugs.OAuthScopesPlug + alias Pleroma.Config.ConfigurableFromDatabase plug(Pleroma.Web.ApiSpec.CastAndValidate) plug(OAuthScopesPlug, %{scopes: ["admin:write"]} when action == :update) @@ -71,7 +72,11 @@ defp translate_children(item, _path) do end def descriptions(conn, _params) do - descriptions = Enum.filter(Pleroma.Docs.JSON.compiled_descriptions(), &whitelisted_config?/1) + descriptions = + Enum.filter( + Pleroma.Docs.JSON.compiled_descriptions(), + &ConfigurableFromDatabase.whitelisted_config?/1 + ) json(conn, translate_descriptions(descriptions)) end @@ -132,7 +137,7 @@ def update(%{body_params: %{configs: configs}} = conn, _) do with :ok <- configurable_from_database() do results = configs - |> Enum.filter(&whitelisted_config?/1) + |> Enum.filter(&ConfigurableFromDatabase.whitelisted_config?/1) |> Enum.map(fn %{group: group, key: key, delete: true} = params -> ConfigDB.delete(%{group: group, key: key, subkeys: params[:subkeys]}) @@ -167,29 +172,10 @@ def update(%{body_params: %{configs: configs}} = conn, _) do end defp configurable_from_database do - if Config.get(:configurable_from_database) do + if ConfigurableFromDatabase.enabled() do :ok else {:error, "You must enable configurable_from_database in your config file."} end end - - defp whitelisted_config?(group, key) do - Pleroma.Config.ConfigurableFromDatabase.allowed_groups() - |> Enum.any?(fn - {whitelisted_group} -> - group == inspect(whitelisted_group) - - {whitelisted_group, whitelisted_key} -> - group == inspect(whitelisted_group) && key == inspect(whitelisted_key) - end) - end - - defp whitelisted_config?(%{group: group, key: key}) do - whitelisted_config?(group, key) - end - - defp whitelisted_config?(%{group: group} = config) do - whitelisted_config?(group, config[:key]) - end end