logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://anongit.hacktivis.me/git/pleroma.git/

dnsrbl_policy.ex (4113B)


  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Web.ActivityPub.MRF.DNSRBLPolicy do
  5. @moduledoc """
  6. Dynamic activity filtering based on an RBL database
  7. This MRF makes queries to a custom DNS server which will
  8. respond with values indicating the classification of the domain
  9. the activity originated from. This method has been widely used
  10. in the email anti-spam industry for very fast reputation checks.
  11. e.g., if the DNS response is 127.0.0.1 or empty, the domain is OK
  12. Other values such as 127.0.0.2 may be used for specific classifications.
  13. Information for why the host is blocked can be stored in a corresponding TXT record.
  14. This method is fail-open so if the queries fail the activites are accepted.
  15. An example of software meant for this purpsoe is rbldnsd which can be found
  16. at http://www.corpit.ru/mjt/rbldnsd.html or mirrored at
  17. https://git.pleroma.social/feld/rbldnsd
  18. It is highly recommended that you run your own copy of rbldnsd and use an
  19. external mechanism to sync/share the contents of the zone file. This is
  20. important to keep the latency on the queries as low as possible and prevent
  21. your DNS server from being attacked so it fails and content is permitted.
  22. """
  23. @behaviour Pleroma.Web.ActivityPub.MRF.Policy
  24. alias Pleroma.Config
  25. require Logger
  26. @query_retries 1
  27. @query_timeout 500
  28. @impl true
  29. def filter(%{"actor" => actor} = activity) do
  30. actor_info = URI.parse(actor)
  31. with {:ok, activity} <- check_rbl(actor_info, activity) do
  32. {:ok, activity}
  33. else
  34. _ -> {:reject, "[DNSRBLPolicy]"}
  35. end
  36. end
  37. @impl true
  38. def filter(activity), do: {:ok, activity}
  39. @impl true
  40. def describe do
  41. mrf_dnsrbl =
  42. Config.get(:mrf_dnsrbl)
  43. |> Enum.into(%{})
  44. {:ok, %{mrf_dnsrbl: mrf_dnsrbl}}
  45. end
  46. @impl true
  47. def config_description do
  48. %{
  49. key: :mrf_dnsrbl,
  50. related_policy: "Pleroma.Web.ActivityPub.MRF.DNSRBLPolicy",
  51. label: "MRF DNSRBL",
  52. description: "DNS RealTime Blackhole Policy",
  53. children: [
  54. %{
  55. key: :nameserver,
  56. type: {:string},
  57. description: "DNSRBL Nameserver to Query (IP or hostame)",
  58. suggestions: ["127.0.0.1"]
  59. },
  60. %{
  61. key: :port,
  62. type: {:string},
  63. description: "Nameserver port",
  64. suggestions: ["53"]
  65. },
  66. %{
  67. key: :zone,
  68. type: {:string},
  69. description: "Root zone for querying",
  70. suggestions: ["bl.pleroma.com"]
  71. }
  72. ]
  73. }
  74. end
  75. defp check_rbl(%{host: actor_host}, activity) do
  76. with false <- match?(^actor_host, Pleroma.Web.Endpoint.host()),
  77. zone when not is_nil(zone) <- Keyword.get(Config.get([:mrf_dnsrbl]), :zone) do
  78. query =
  79. Enum.join([actor_host, zone], ".")
  80. |> String.to_charlist()
  81. rbl_response = rblquery(query)
  82. if Enum.empty?(rbl_response) do
  83. {:ok, activity}
  84. else
  85. Task.start(fn ->
  86. reason =
  87. case rblquery(query, :txt) do
  88. [[result]] -> result
  89. _ -> "undefined"
  90. end
  91. Logger.warning(
  92. "DNSRBL Rejected activity from #{actor_host} for reason: #{inspect(reason)}"
  93. )
  94. end)
  95. :error
  96. end
  97. else
  98. _ -> {:ok, activity}
  99. end
  100. end
  101. defp get_rblhost_ip(rblhost) do
  102. case rblhost |> String.to_charlist() |> :inet_parse.address() do
  103. {:ok, _} -> rblhost |> String.to_charlist() |> :inet_parse.address()
  104. _ -> {:ok, rblhost |> String.to_charlist() |> :inet_res.lookup(:in, :a) |> Enum.random()}
  105. end
  106. end
  107. defp rblquery(query, type \\ :a) do
  108. config = Config.get([:mrf_dnsrbl])
  109. case get_rblhost_ip(config[:nameserver]) do
  110. {:ok, rblnsip} ->
  111. :inet_res.lookup(query, :in, type,
  112. nameservers: [{rblnsip, config[:port]}],
  113. timeout: @query_timeout,
  114. retry: @query_retries
  115. )
  116. _ ->
  117. []
  118. end
  119. end
  120. end