From e10db52e0a1c9cc24803a406998a9cfe75b7f9f2 Mon Sep 17 00:00:00 2001 From: Mint Date: Fri, 13 Sep 2024 02:58:59 +0300 Subject: [PATCH 01/28] Add dependencies for Swoosh's Mua mail adapter --- changelog.d/swoosh-mua.add | 1 + mix.exs | 4 +++- mix.lock | 4 +++- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 changelog.d/swoosh-mua.add diff --git a/changelog.d/swoosh-mua.add b/changelog.d/swoosh-mua.add new file mode 100644 index 0000000000..d4c4bbd084 --- /dev/null +++ b/changelog.d/swoosh-mua.add @@ -0,0 +1 @@ +Added dependencies for Swoosh's Mua mail adapter diff --git a/mix.exs b/mix.exs index 0d49a6b45f..ceae5c26df 100644 --- a/mix.exs +++ b/mix.exs @@ -153,7 +153,7 @@ defp deps do {:calendar, "~> 1.0"}, {:cachex, "~> 3.2"}, {:tesla, "~> 1.11"}, - {:castore, "~> 0.1"}, + {:castore, "~> 1.0"}, {:cowlib, "~> 2.9", override: true}, {:gun, "~> 2.0.0-rc.1", override: true}, {:finch, "~> 0.15"}, @@ -169,6 +169,8 @@ defp deps do {:swoosh, "~> 1.16.9"}, {:phoenix_swoosh, "~> 1.1"}, {:gen_smtp, "~> 0.13"}, + {:mua, "~> 0.2.0"}, + {:mail, "~> 0.3.0"}, {:ex_syslogger, "~> 1.4"}, {:floki, "~> 0.35"}, {:timex, "~> 3.6"}, diff --git a/mix.lock b/mix.lock index 01f2eef98e..2cf44862b4 100644 --- a/mix.lock +++ b/mix.lock @@ -11,7 +11,7 @@ "cachex": {:hex, :cachex, "3.6.0", "14a1bfbeee060dd9bec25a5b6f4e4691e3670ebda28c8ba2884b12fe30b36bf8", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "ebf24e373883bc8e0c8d894a63bbe102ae13d918f790121f5cfe6e485cc8e2e2"}, "calendar": {:hex, :calendar, "1.0.0", "f52073a708528482ec33d0a171954ca610fe2bd28f1e871f247dc7f1565fa807", [:mix], [{:tzdata, "~> 0.5.20 or ~> 0.1.201603 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "990e9581920c82912a5ee50e62ff5ef96da6b15949a2ee4734f935fdef0f0a6f"}, "captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "6630c42aaaab124e697b4e513190c89d8b64e410", [ref: "6630c42aaaab124e697b4e513190c89d8b64e410"]}, - "castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"}, + "castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"}, "cc_precompiler": {:hex, :cc_precompiler, "0.1.9", "e8d3364f310da6ce6463c3dd20cf90ae7bbecbf6c5203b98bf9b48035592649b", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "9dcab3d0f3038621f1601f13539e7a9ee99843862e66ad62827b0c42b2f58a54"}, "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"}, @@ -72,6 +72,7 @@ "jumper": {:hex, :jumper, "1.0.2", "68cdcd84472a00ac596b4e6459a41b3062d4427cbd4f1e8c8793c5b54f1406a7", [:mix], [], "hexpm", "9b7782409021e01ab3c08270e26f36eb62976a38c1aa64b2eaf6348422f165e1"}, "linkify": {:hex, :linkify, "0.5.3", "5f8143d8f61f5ff08d3aeeff47ef6509492b4948d8f08007fbf66e4d2246a7f2", [:mix], [], "hexpm", "3ef35a1377d47c25506e07c1c005ea9d38d700699d92ee92825f024434258177"}, "logger_backends": {:hex, :logger_backends, "1.0.0", "09c4fad6202e08cb0fbd37f328282f16539aca380f512523ce9472b28edc6bdf", [:mix], [], "hexpm", "1faceb3e7ec3ef66a8f5746c5afd020e63996df6fd4eb8cdb789e5665ae6c9ce"}, + "mail": {:hex, :mail, "0.3.1", "cb0a14e4ed8904e4e5a08214e686ccf6f9099346885db17d8c309381f865cc5c", [:mix], [], "hexpm", "1db701e89865c1d5fa296b2b57b1cd587587cca8d8a1a22892b35ef5a8e352a6"}, "majic": {:hex, :majic, "1.0.0", "37e50648db5f5c2ff0c9fb46454d034d11596c03683807b9fb3850676ffdaab3", [:make, :mix], [{:elixir_make, "~> 0.6.1", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "7905858f76650d49695f14ea55cd9aaaee0c6654fa391671d4cf305c275a0a9e"}, "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"}, "makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"}, @@ -85,6 +86,7 @@ "mock": {:hex, :mock, "0.3.8", "7046a306b71db2488ef54395eeb74df0a7f335a7caca4a3d3875d1fc81c884dd", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "7fa82364c97617d79bb7d15571193fc0c4fe5afd0c932cef09426b3ee6fe2022"}, "mogrify": {:hex, :mogrify, "0.9.3", "238c782f00271dace01369ad35ae2e9dd020feee3443b9299ea5ea6bed559841", [:mix], [], "hexpm", "0189b1e1de27455f2b9ae8cf88239cefd23d38de9276eb5add7159aea51731e6"}, "mox": {:hex, :mox, "1.1.0", "0f5e399649ce9ab7602f72e718305c0f9cdc351190f72844599545e4996af73c", [:mix], [], "hexpm", "d44474c50be02d5b72131070281a5d3895c0e7a95c780e90bc0cfe712f633a13"}, + "mua": {:hex, :mua, "0.2.3", "46b29b7b2bb14105c0b7be9526f7c452df17a7841b30b69871c024a822ff551c", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "7fe861a87fcc06a980d3941bbcb2634e5f0f30fd6ad15ef6c0423ff9dc7e46de"}, "multipart": {:hex, :multipart, "0.4.0", "634880a2148d4555d050963373d0e3bbb44a55b2badd87fa8623166172e9cda0", [:mix], [{:mime, "~> 1.2 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm", "3c5604bc2fb17b3137e5d2abdf5dacc2647e60c5cc6634b102cf1aef75a06f0a"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_parsec": {:hex, :nimble_parsec, "0.6.0", "32111b3bf39137144abd7ba1cce0914533b2d16ef35e8abc5ec8be6122944263", [:mix], [], "hexpm", "27eac315a94909d4dc68bc07a4a83e06c8379237c5ea528a9acff4ca1c873c52"}, From 5539fea3bb0d272b4cefc2b72755cb3cd285cc67 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sat, 14 Sep 2024 20:03:26 -0400 Subject: [PATCH 02/28] LDAP: permit overriding the CA root --- changelog.d/ldap-ca.add | 1 + config/config.exs | 4 +++- docs/configuration/cheatsheet.md | 1 + lib/pleroma/web/auth/ldap_authenticator.ex | 17 ++++++++++++++++- mix.exs | 1 - 5 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 changelog.d/ldap-ca.add diff --git a/changelog.d/ldap-ca.add b/changelog.d/ldap-ca.add new file mode 100644 index 0000000000..32ecbb5c02 --- /dev/null +++ b/changelog.d/ldap-ca.add @@ -0,0 +1 @@ +LDAP configuration now permits overriding the CA root certificate file for TLS validation. diff --git a/config/config.exs b/config/config.exs index 80a3b8d57f..237928503f 100644 --- a/config/config.exs +++ b/config/config.exs @@ -619,7 +619,9 @@ tls: System.get_env("LDAP_TLS") == "true", tlsopts: [], base: System.get_env("LDAP_BASE") || "dc=example,dc=com", - uid: System.get_env("LDAP_UID") || "cn" + uid: System.get_env("LDAP_UID") || "cn", + # defaults to CAStore's Mozilla roots + cacertfile: nil oauth_consumer_strategies = System.get_env("OAUTH_CONSUMER_STRATEGIES") diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index 0b4e53b6f5..4cbde696ee 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -974,6 +974,7 @@ Pleroma account will be created with the same name as the LDAP user name. * `tlsopts`: additional TLS options * `base`: LDAP base, e.g. "dc=example,dc=com" * `uid`: LDAP attribute name to authenticate the user, e.g. when "cn", the filter will be "cn=username,base" +* `cacertfile`: Path to alternate CA root certificates file Note, if your LDAP server is an Active Directory server the correct value is commonly `uid: "cn"`, but if you use an OpenLDAP server the value may be `uid: "uid"`. diff --git a/lib/pleroma/web/auth/ldap_authenticator.ex b/lib/pleroma/web/auth/ldap_authenticator.ex index d31f347479..7f2cd3d692 100644 --- a/lib/pleroma/web/auth/ldap_authenticator.ex +++ b/lib/pleroma/web/auth/ldap_authenticator.ex @@ -42,11 +42,14 @@ defp ldap_user(name, password) do ssl = Keyword.get(ldap, :ssl, false) sslopts = Keyword.get(ldap, :sslopts, []) tlsopts = Keyword.get(ldap, :tlsopts, []) + cacertfile = Keyword.get(ldap, :cacertfile) || CAStore.file_path() options = [{:port, port}, {:ssl, ssl}, {:timeout, @connection_timeout}] ++ if sslopts != [], do: [{:sslopts, sslopts}], else: [] + cacerts = decode_certfile(cacertfile) + case :eldap.open([to_charlist(host)], options) do {:ok, connection} -> try do @@ -58,7 +61,7 @@ defp ldap_user(name, password) do Keyword.merge( [ verify: :verify_peer, - cacerts: :certifi.cacerts(), + cacerts: cacerts, customize_hostname_check: [ fqdn_fun: fn _ -> to_charlist(host) end ] @@ -147,4 +150,16 @@ defp try_register(name, attributes) do error -> error end end + + defp decode_certfile(file) do + with {:ok, data} <- File.read(file) do + data + |> :public_key.pem_decode() + |> Enum.map(fn {_, b, _} -> b end) + else + _ -> + Logger.error("Unable to read certfile: #{file}") + [] + end + end end diff --git a/mix.exs b/mix.exs index 9a261547f3..0d49a6b45f 100644 --- a/mix.exs +++ b/mix.exs @@ -204,7 +204,6 @@ defp deps do {:oban_live_dashboard, "~> 0.1.1"}, {:multipart, "~> 0.4.0", optional: true}, {:argon2_elixir, "~> 4.0"}, - {:certifi, "~> 2.12"}, ## dev & test {:phoenix_live_reload, "~> 1.3.3", only: :dev}, From af3bf8a4628c0b2981d69f624e3be298adc7dfe6 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sun, 15 Sep 2024 13:56:16 -0400 Subject: [PATCH 03/28] Support implicit TLS connections Update docs to clarify that the :ssl option is also for modern TLS, but the :tls option is only for STARTTLS These options may benefit from being renamed but they match upstream terminology. --- changelog.d/ldaps.fix | 1 + docs/configuration/cheatsheet.md | 4 +- lib/pleroma/web/auth/ldap_authenticator.ex | 50 ++++++++++++---------- 3 files changed, 31 insertions(+), 24 deletions(-) create mode 100644 changelog.d/ldaps.fix diff --git a/changelog.d/ldaps.fix b/changelog.d/ldaps.fix new file mode 100644 index 0000000000..a1dc901ab0 --- /dev/null +++ b/changelog.d/ldaps.fix @@ -0,0 +1 @@ +LDAPS connections (implicit TLS) are now supported. diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index 4cbde696ee..6a535e054f 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -968,9 +968,9 @@ Pleroma account will be created with the same name as the LDAP user name. * `enabled`: enables LDAP authentication * `host`: LDAP server hostname * `port`: LDAP port, e.g. 389 or 636 -* `ssl`: true to use SSL, usually implies the port 636 +* `ssl`: true to use implicit SSL/TLS, usually port 636 * `sslopts`: additional SSL options -* `tls`: true to start TLS, usually implies the port 389 +* `tls`: true to use explicit TLS (STARTTLS), usually port 389 * `tlsopts`: additional TLS options * `base`: LDAP base, e.g. "dc=example,dc=com" * `uid`: LDAP attribute name to authenticate the user, e.g. when "cn", the filter will be "cn=username,base" diff --git a/lib/pleroma/web/auth/ldap_authenticator.ex b/lib/pleroma/web/auth/ldap_authenticator.ex index 7f2cd3d692..18a4e81ee4 100644 --- a/lib/pleroma/web/auth/ldap_authenticator.ex +++ b/lib/pleroma/web/auth/ldap_authenticator.ex @@ -40,34 +40,39 @@ defp ldap_user(name, password) do host = Keyword.get(ldap, :host, "localhost") port = Keyword.get(ldap, :port, 389) ssl = Keyword.get(ldap, :ssl, false) - sslopts = Keyword.get(ldap, :sslopts, []) - tlsopts = Keyword.get(ldap, :tlsopts, []) + tls = Keyword.get(ldap, :tls, false) cacertfile = Keyword.get(ldap, :cacertfile) || CAStore.file_path() - options = - [{:port, port}, {:ssl, ssl}, {:timeout, @connection_timeout}] ++ - if sslopts != [], do: [{:sslopts, sslopts}], else: [] + default_secure_opts = [ + verify: :verify_peer, + cacerts: decode_certfile(cacertfile), + customize_hostname_check: [ + fqdn_fun: fn _ -> to_charlist(host) end + ] + ] - cacerts = decode_certfile(cacertfile) + sslopts = Keyword.merge(default_secure_opts, Keyword.get(ldap, :sslopts, [])) + tlsopts = Keyword.merge(default_secure_opts, Keyword.get(ldap, :tlsopts, [])) + + # :sslopts can only be included in :eldap.open/2 when {ssl: true} + # or the connection will fail + options = + if ssl do + [{:port, port}, {:ssl, ssl}, {:sslopts, sslopts}, {:timeout, @connection_timeout}] + else + [{:port, port}, {:ssl, ssl}, {:timeout, @connection_timeout}] + end case :eldap.open([to_charlist(host)], options) do {:ok, connection} -> - try do - if Keyword.get(ldap, :tls, false) do + cond do + ssl -> :application.ensure_all_started(:ssl) + tls -> case :eldap.start_tls( connection, - Keyword.merge( - [ - verify: :verify_peer, - cacerts: cacerts, - customize_hostname_check: [ - fqdn_fun: fn _ -> to_charlist(host) end - ] - ], - tlsopts - ), + tlsopts, @connection_timeout ) do :ok -> @@ -75,14 +80,15 @@ defp ldap_user(name, password) do error -> Logger.error("Could not start TLS: #{inspect(error)}") + :eldap.close(connection) end - end - bind_user(connection, ldap, name, password) - after - :eldap.close(connection) + true -> + :ok end + bind_user(connection, ldap, name, password) + {:error, error} -> Logger.error("Could not open LDAP connection: #{inspect(error)}") {:error, {:ldap_connection_error, error}} From 91d1d7260b7084f59ae42e7c4b46c7fb963fda96 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Sun, 15 Sep 2024 23:18:17 -0400 Subject: [PATCH 04/28] Retain the try do so an LDAP failure can fall back to local database. This fixes tests but the automatic fallback may not be well documented behavior. --- lib/pleroma/web/auth/ldap_authenticator.ex | 42 ++++++++++++---------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/lib/pleroma/web/auth/ldap_authenticator.ex b/lib/pleroma/web/auth/ldap_authenticator.ex index 18a4e81ee4..ad5bc9863f 100644 --- a/lib/pleroma/web/auth/ldap_authenticator.ex +++ b/lib/pleroma/web/auth/ldap_authenticator.ex @@ -65,30 +65,34 @@ defp ldap_user(name, password) do case :eldap.open([to_charlist(host)], options) do {:ok, connection} -> - cond do - ssl -> - :application.ensure_all_started(:ssl) + try do + cond do + ssl -> + :application.ensure_all_started(:ssl) - tls -> - case :eldap.start_tls( - connection, - tlsopts, - @connection_timeout - ) do - :ok -> - :ok + tls -> + case :eldap.start_tls( + connection, + tlsopts, + @connection_timeout + ) do + :ok -> + :ok - error -> - Logger.error("Could not start TLS: #{inspect(error)}") - :eldap.close(connection) - end + error -> + Logger.error("Could not start TLS: #{inspect(error)}") + :eldap.close(connection) + end - true -> - :ok + true -> + :ok + end + + bind_user(connection, ldap, name, password) + after + :eldap.close(connection) end - bind_user(connection, ldap, name, password) - {:error, error} -> Logger.error("Could not open LDAP connection: #{inspect(error)}") {:error, {:ldap_connection_error, error}} From e59706c201bd71525c0a15008c3cb5dcdfb73289 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 16 Sep 2024 11:39:19 -0400 Subject: [PATCH 05/28] Reapply "Custom mix task to retry failed tests once in CI pipeline" This reverts commit b281ad06de2de331450a5e319e3ba497071d4197. --- .gitlab-ci.yml | 2 +- changelog.d/fix-test-failures.skip | 0 lib/mix/tasks/pleroma/test_runner.ex | 25 +++++++++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) delete mode 100644 changelog.d/fix-test-failures.skip create mode 100644 lib/mix/tasks/pleroma/test_runner.ex diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1e04dae76f..76d1a4210d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -134,7 +134,7 @@ unit-testing-1.13.4-otp-25: script: &testing_script - mix ecto.create - mix ecto.migrate - - mix test --cover --preload-modules + - mix pleroma.test_runner --cover --preload-modules coverage: '/^Line total: ([^ ]*%)$/' artifacts: reports: diff --git a/changelog.d/fix-test-failures.skip b/changelog.d/fix-test-failures.skip deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/lib/mix/tasks/pleroma/test_runner.ex b/lib/mix/tasks/pleroma/test_runner.ex new file mode 100644 index 0000000000..69fefb0014 --- /dev/null +++ b/lib/mix/tasks/pleroma/test_runner.ex @@ -0,0 +1,25 @@ +defmodule Mix.Tasks.Pleroma.TestRunner do + @shortdoc "Retries tests once if they fail" + + use Mix.Task + + def run(args \\ []) do + case System.cmd("mix", ["test"] ++ args, into: IO.stream(:stdio, :line)) do + {_, 0} -> + :ok + + _ -> + retry(args) + end + end + + def retry(args) do + case System.cmd("mix", ["test", "--failed"] ++ args, into: IO.stream(:stdio, :line)) do + {_, 0} -> + :ok + + _ -> + exit(1) + end + end +end From 9264b21907f5c6890694d6d611ade9b13433463a Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 16 Sep 2024 00:26:57 -0400 Subject: [PATCH 06/28] Pleroma.LDAP This adds a GenServer which will keep an LDAP connection open and auto reconnect on failure with a 5 second wait between retries. Another benefit is this prevents parsing the Root CAs for every login attempt as we only need to do it once per connection. --- lib/pleroma/application.ex | 1 + lib/pleroma/ldap.ex | 233 +++++++++++++++++++++ lib/pleroma/web/auth/ldap_authenticator.ex | 147 +------------ 3 files changed, 236 insertions(+), 145 deletions(-) create mode 100644 lib/pleroma/ldap.ex diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index cb15dc1e9a..3f199c002d 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -94,6 +94,7 @@ def start(_type, _args) do children = [ Pleroma.PromEx, + Pleroma.LDAP, Pleroma.Repo, Config.TransferTask, Pleroma.Emoji, diff --git a/lib/pleroma/ldap.ex b/lib/pleroma/ldap.ex new file mode 100644 index 0000000000..8689325012 --- /dev/null +++ b/lib/pleroma/ldap.ex @@ -0,0 +1,233 @@ +defmodule Pleroma.LDAP do + use GenServer + + require Logger + + alias Pleroma.Config + alias Pleroma.User + + import Pleroma.Web.Auth.Helpers, only: [fetch_user: 1] + + @connection_timeout 10_000 + @search_timeout 10_000 + + def start_link(_) do + GenServer.start_link(__MODULE__, [], name: __MODULE__) + end + + @impl true + def init(state) do + case {Config.get(Pleroma.Web.Auth.Authenticator), Config.get([:ldap, :enabled])} do + {Pleroma.Web.Auth.LDAPAuthenticator, true} -> + {:ok, state, {:continue, :connect}} + + {Pleroma.Web.Auth.LDAPAuthenticator, false} -> + Logger.error( + "LDAP Authenticator enabled but :pleroma, :ldap is not enabled. Auth will not work." + ) + + {:ok, state} + + {_, true} -> + Logger.warning( + ":pleroma, :ldap is enabled but Pleroma.Web.Authenticator is not set to the LDAPAuthenticator. LDAP will not be used." + ) + + {:ok, state} + end + end + + @impl true + def handle_continue(:connect, _state), do: do_handle_connect() + + @impl true + def handle_info(:connect, _state), do: do_handle_connect() + + def handle_info({:bind_after_reconnect, name, password, from}, state) do + result = bind_user(state[:connection], name, password) + + GenServer.reply(from, result) + + {:noreply, state} + end + + defp do_handle_connect() do + state = + case connect() do + {:ok, connection} -> + :eldap.controlling_process(connection, self()) + [connection: connection] + + _ -> + Logger.error("Failed to connect to LDAP. Retrying in 5000ms") + Process.send_after(self(), :connect, 5_000) + [] + end + + {:noreply, state} + end + + @impl true + def handle_call({:bind_user, name, password}, from, state) do + case bind_user(state[:connection], name, password) do + :needs_reconnect -> + Process.send(self(), {:bind_after_reconnect, name, password, from}, []) + {:noreply, state, {:continue, :connect}} + + result -> + {:reply, result, state, :hibernate} + end + end + + @impl true + def terminate(_, state) do + :eldap.close(state[:connection]) + + :ok + end + + defp connect() do + ldap = Config.get(:ldap, []) + host = Keyword.get(ldap, :host, "localhost") + port = Keyword.get(ldap, :port, 389) + ssl = Keyword.get(ldap, :ssl, false) + tls = Keyword.get(ldap, :tls, false) + cacertfile = Keyword.get(ldap, :cacertfile) || CAStore.file_path() + + default_secure_opts = [ + verify: :verify_peer, + cacerts: decode_certfile(cacertfile), + customize_hostname_check: [ + fqdn_fun: fn _ -> to_charlist(host) end + ] + ] + + sslopts = Keyword.merge(default_secure_opts, Keyword.get(ldap, :sslopts, [])) + tlsopts = Keyword.merge(default_secure_opts, Keyword.get(ldap, :tlsopts, [])) + + default_options = [{:port, port}, {:ssl, ssl}, {:timeout, @connection_timeout}] + + # :sslopts can only be included in :eldap.open/2 when {ssl: true} + # or the connection will fail + options = + if ssl do + default_options ++ [{:sslopts, sslopts}] + else + default_options + end + + case :eldap.open([to_charlist(host)], options) do + {:ok, connection} -> + try do + cond do + ssl -> + :application.ensure_all_started(:ssl) + {:ok, connection} + + tls -> + case :eldap.start_tls( + connection, + tlsopts, + @connection_timeout + ) do + :ok -> + {:ok, connection} + + error -> + Logger.error("Could not start TLS: #{inspect(error)}") + :eldap.close(connection) + end + + true -> + {:ok, :connection} + end + after + :ok + end + + {:error, error} -> + Logger.error("Could not open LDAP connection: #{inspect(error)}") + {:error, {:ldap_connection_error, error}} + end + end + + defp bind_user(connection, name, password) do + uid = Config.get([:ldap, :uid], "cn") + base = Config.get([:ldap, :base]) + + case :eldap.simple_bind(connection, "#{uid}=#{name},#{base}", password) do + :ok -> + case fetch_user(name) do + %User{} = user -> + user + + _ -> + register_user(connection, base, uid, name) + end + + # eldap does not inform us of socket closure + # until it is used + {:error, {:gen_tcp_error, :closed}} -> + :eldap.close(connection) + :needs_reconnect + + error -> + Logger.error("Could not bind LDAP user #{name}: #{inspect(error)}") + {:error, {:ldap_bind_error, error}} + end + end + + defp register_user(connection, base, uid, name) do + case :eldap.search(connection, [ + {:base, to_charlist(base)}, + {:filter, :eldap.equalityMatch(to_charlist(uid), to_charlist(name))}, + {:scope, :eldap.wholeSubtree()}, + {:timeout, @search_timeout} + ]) do + # The :eldap_search_result record structure changed in OTP 24.3 and added a controls field + # https://github.com/erlang/otp/pull/5538 + {:ok, {:eldap_search_result, [{:eldap_entry, _object, attributes}], _referrals}} -> + try_register(name, attributes) + + {:ok, {:eldap_search_result, [{:eldap_entry, _object, attributes}], _referrals, _controls}} -> + try_register(name, attributes) + + error -> + Logger.error("Couldn't register user because LDAP search failed: #{inspect(error)}") + {:error, {:ldap_search_error, error}} + end + end + + defp try_register(name, attributes) do + params = %{ + name: name, + nickname: name, + password: nil + } + + params = + case List.keyfind(attributes, ~c"mail", 0) do + {_, [mail]} -> Map.put_new(params, :email, :erlang.list_to_binary(mail)) + _ -> params + end + + changeset = User.register_changeset_ldap(%User{}, params) + + case User.register(changeset) do + {:ok, user} -> user + error -> error + end + end + + defp decode_certfile(file) do + with {:ok, data} <- File.read(file) do + data + |> :public_key.pem_decode() + |> Enum.map(fn {_, b, _} -> b end) + else + _ -> + Logger.error("Unable to read certfile: #{file}") + [] + end + end +end diff --git a/lib/pleroma/web/auth/ldap_authenticator.ex b/lib/pleroma/web/auth/ldap_authenticator.ex index ad5bc9863f..c420c8bc30 100644 --- a/lib/pleroma/web/auth/ldap_authenticator.ex +++ b/lib/pleroma/web/auth/ldap_authenticator.ex @@ -5,16 +5,11 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do alias Pleroma.User - require Logger - - import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1, fetch_user: 1] + import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1] @behaviour Pleroma.Web.Auth.Authenticator @base Pleroma.Web.Auth.PleromaAuthenticator - @connection_timeout 10_000 - @search_timeout 10_000 - defdelegate get_registration(conn), to: @base defdelegate create_from_registration(conn, registration), to: @base defdelegate handle_error(conn, error), to: @base @@ -24,7 +19,7 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do def get_user(%Plug.Conn{} = conn) do with {:ldap, true} <- {:ldap, Pleroma.Config.get([:ldap, :enabled])}, {:ok, {name, password}} <- fetch_credentials(conn), - %User{} = user <- ldap_user(name, password) do + %User{} = user <- GenServer.call(Pleroma.LDAP, {:bind_user, name, password}) do {:ok, user} else {:ldap, _} -> @@ -34,142 +29,4 @@ def get_user(%Plug.Conn{} = conn) do error end end - - defp ldap_user(name, password) do - ldap = Pleroma.Config.get(:ldap, []) - host = Keyword.get(ldap, :host, "localhost") - port = Keyword.get(ldap, :port, 389) - ssl = Keyword.get(ldap, :ssl, false) - tls = Keyword.get(ldap, :tls, false) - cacertfile = Keyword.get(ldap, :cacertfile) || CAStore.file_path() - - default_secure_opts = [ - verify: :verify_peer, - cacerts: decode_certfile(cacertfile), - customize_hostname_check: [ - fqdn_fun: fn _ -> to_charlist(host) end - ] - ] - - sslopts = Keyword.merge(default_secure_opts, Keyword.get(ldap, :sslopts, [])) - tlsopts = Keyword.merge(default_secure_opts, Keyword.get(ldap, :tlsopts, [])) - - # :sslopts can only be included in :eldap.open/2 when {ssl: true} - # or the connection will fail - options = - if ssl do - [{:port, port}, {:ssl, ssl}, {:sslopts, sslopts}, {:timeout, @connection_timeout}] - else - [{:port, port}, {:ssl, ssl}, {:timeout, @connection_timeout}] - end - - case :eldap.open([to_charlist(host)], options) do - {:ok, connection} -> - try do - cond do - ssl -> - :application.ensure_all_started(:ssl) - - tls -> - case :eldap.start_tls( - connection, - tlsopts, - @connection_timeout - ) do - :ok -> - :ok - - error -> - Logger.error("Could not start TLS: #{inspect(error)}") - :eldap.close(connection) - end - - true -> - :ok - end - - bind_user(connection, ldap, name, password) - after - :eldap.close(connection) - end - - {:error, error} -> - Logger.error("Could not open LDAP connection: #{inspect(error)}") - {:error, {:ldap_connection_error, error}} - end - end - - defp bind_user(connection, ldap, name, password) do - uid = Keyword.get(ldap, :uid, "cn") - base = Keyword.get(ldap, :base) - - case :eldap.simple_bind(connection, "#{uid}=#{name},#{base}", password) do - :ok -> - case fetch_user(name) do - %User{} = user -> - user - - _ -> - register_user(connection, base, uid, name) - end - - error -> - Logger.error("Could not bind LDAP user #{name}: #{inspect(error)}") - {:error, {:ldap_bind_error, error}} - end - end - - defp register_user(connection, base, uid, name) do - case :eldap.search(connection, [ - {:base, to_charlist(base)}, - {:filter, :eldap.equalityMatch(to_charlist(uid), to_charlist(name))}, - {:scope, :eldap.wholeSubtree()}, - {:timeout, @search_timeout} - ]) do - # The :eldap_search_result record structure changed in OTP 24.3 and added a controls field - # https://github.com/erlang/otp/pull/5538 - {:ok, {:eldap_search_result, [{:eldap_entry, _object, attributes}], _referrals}} -> - try_register(name, attributes) - - {:ok, {:eldap_search_result, [{:eldap_entry, _object, attributes}], _referrals, _controls}} -> - try_register(name, attributes) - - error -> - Logger.error("Couldn't register user because LDAP search failed: #{inspect(error)}") - {:error, {:ldap_search_error, error}} - end - end - - defp try_register(name, attributes) do - params = %{ - name: name, - nickname: name, - password: nil - } - - params = - case List.keyfind(attributes, ~c"mail", 0) do - {_, [mail]} -> Map.put_new(params, :email, :erlang.list_to_binary(mail)) - _ -> params - end - - changeset = User.register_changeset_ldap(%User{}, params) - - case User.register(changeset) do - {:ok, user} -> user - error -> error - end - end - - defp decode_certfile(file) do - with {:ok, data} <- File.read(file) do - data - |> :public_key.pem_decode() - |> Enum.map(fn {_, b, _} -> b end) - else - _ -> - Logger.error("Unable to read certfile: #{file}") - [] - end - end end From ead287d623e83b8d9ffaa327b9edf96e046bfacd Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 16 Sep 2024 13:14:19 -0400 Subject: [PATCH 07/28] Credo --- lib/pleroma/ldap.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/ldap.ex b/lib/pleroma/ldap.ex index 8689325012..11544f0d9e 100644 --- a/lib/pleroma/ldap.ex +++ b/lib/pleroma/ldap.ex @@ -51,7 +51,7 @@ def handle_info({:bind_after_reconnect, name, password, from}, state) do {:noreply, state} end - defp do_handle_connect() do + defp do_handle_connect do state = case connect() do {:ok, connection} -> @@ -86,7 +86,7 @@ def terminate(_, state) do :ok end - defp connect() do + defp connect do ldap = Config.get(:ldap, []) host = Keyword.get(ldap, :host, "localhost") port = Keyword.get(ldap, :port, 389) From 7c04098dde0681f7ad299782bc09eaa9bc3a6bad Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 16 Sep 2024 16:15:53 -0400 Subject: [PATCH 08/28] Catchall for when LDAP is not enabled --- lib/pleroma/ldap.ex | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pleroma/ldap.ex b/lib/pleroma/ldap.ex index 11544f0d9e..ac819613e0 100644 --- a/lib/pleroma/ldap.ex +++ b/lib/pleroma/ldap.ex @@ -34,6 +34,9 @@ def init(state) do ) {:ok, state} + + _ -> + {:ok, state} end end From 44b836c94c1059551fbc7564770001311d2d1e6a Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 16 Sep 2024 16:24:27 -0400 Subject: [PATCH 09/28] Fix tests We do not need to mock and verify connections are closed as the new Pleroma.LDAP GenServer will handle managing the connection lifetime --- .../web/o_auth/ldap_authorization_test.exs | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/test/pleroma/web/o_auth/ldap_authorization_test.exs b/test/pleroma/web/o_auth/ldap_authorization_test.exs index 07ce2eed84..35b947fd04 100644 --- a/test/pleroma/web/o_auth/ldap_authorization_test.exs +++ b/test/pleroma/web/o_auth/ldap_authorization_test.exs @@ -28,11 +28,7 @@ test "authorizes the existing user using LDAP credentials" do {:eldap, [], [ open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:ok, self()} end, - simple_bind: fn _connection, _dn, ^password -> :ok end, - close: fn _connection -> - send(self(), :close_connection) - :ok - end + simple_bind: fn _connection, _dn, ^password -> :ok end ]} ] do conn = @@ -50,7 +46,6 @@ test "authorizes the existing user using LDAP credentials" do token = Repo.get_by(Token, token: token) assert token.user_id == user.id - assert_received :close_connection end end @@ -72,10 +67,6 @@ test "creates a new user after successful LDAP authorization" do wholeSubtree: fn -> :ok end, search: fn _connection, _options -> {:ok, {:eldap_search_result, [{:eldap_entry, ~c"", []}], []}} - end, - close: fn _connection -> - send(self(), :close_connection) - :ok end ]} ] do @@ -94,7 +85,6 @@ test "creates a new user after successful LDAP authorization" do token = Repo.get_by(Token, token: token) |> Repo.preload(:user) assert token.user.nickname == user.nickname - assert_received :close_connection end end @@ -111,11 +101,7 @@ test "disallow authorization for wrong LDAP credentials" do {:eldap, [], [ open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:ok, self()} end, - simple_bind: fn _connection, _dn, ^password -> {:error, :invalidCredentials} end, - close: fn _connection -> - send(self(), :close_connection) - :ok - end + simple_bind: fn _connection, _dn, ^password -> {:error, :invalidCredentials} end ]} ] do conn = @@ -129,7 +115,6 @@ test "disallow authorization for wrong LDAP credentials" do }) assert %{"error" => "Invalid credentials"} = json_response(conn, 400) - assert_received :close_connection end end end From d82abf925ddbe8b98ba8191713115db50c38a0c0 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 16 Sep 2024 16:25:44 -0400 Subject: [PATCH 10/28] Ensure :cacertfile is configurable in ConfigDB --- config/description.exs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/description.exs b/config/description.exs index 15faecb38c..ade47b7e02 100644 --- a/config/description.exs +++ b/config/description.exs @@ -2297,6 +2297,12 @@ description: "LDAP attribute name to authenticate the user, e.g. when \"cn\", the filter will be \"cn=username,base\"", suggestions: ["cn"] + }, + %{ + key: :cacertfile, + label: "CACertfile", + type: :string, + description: "Path to CA certificate file" } ] }, From 65a7b387c35b4913b6109692a84bae80af8b9a96 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 16 Sep 2024 16:28:37 -0400 Subject: [PATCH 11/28] Require a reboot if LDAP configuration changes --- lib/pleroma/config/transfer_task.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/config/transfer_task.ex b/lib/pleroma/config/transfer_task.ex index ffc95f1447..140dd77111 100644 --- a/lib/pleroma/config/transfer_task.ex +++ b/lib/pleroma/config/transfer_task.ex @@ -22,7 +22,8 @@ defp reboot_time_keys, {:pleroma, :markup}, {:pleroma, :streamer}, {:pleroma, :pools}, - {:pleroma, :connections_pool} + {:pleroma, :connections_pool}, + {:pleroma, :ldap} ] defp reboot_time_subkeys, From 123093a1868b25f101dfc4b02895c22a0daf5733 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 17 Sep 2024 13:07:26 -0400 Subject: [PATCH 12/28] Ensure :ssl is started before we attempt to make the LDAP connection --- lib/pleroma/ldap.ex | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/pleroma/ldap.ex b/lib/pleroma/ldap.ex index ac819613e0..042a4daa21 100644 --- a/lib/pleroma/ldap.ex +++ b/lib/pleroma/ldap.ex @@ -97,6 +97,8 @@ defp connect do tls = Keyword.get(ldap, :tls, false) cacertfile = Keyword.get(ldap, :cacertfile) || CAStore.file_path() + if ssl, do: Application.ensure_all_started(:ssl) + default_secure_opts = [ verify: :verify_peer, cacerts: decode_certfile(cacertfile), @@ -123,10 +125,6 @@ defp connect do {:ok, connection} -> try do cond do - ssl -> - :application.ensure_all_started(:ssl) - {:ok, connection} - tls -> case :eldap.start_tls( connection, From d0ee899ab94788e37e6ac3c43342017d1b27903a Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 17 Sep 2024 13:14:09 -0400 Subject: [PATCH 13/28] Only close connection if it is not nil --- lib/pleroma/ldap.ex | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/ldap.ex b/lib/pleroma/ldap.ex index 042a4daa21..3df20fe094 100644 --- a/lib/pleroma/ldap.ex +++ b/lib/pleroma/ldap.ex @@ -84,7 +84,11 @@ def handle_call({:bind_user, name, password}, from, state) do @impl true def terminate(_, state) do - :eldap.close(state[:connection]) + connection = Keyword.get(state, :connection) + + if not is_nil(connection) do + :eldap.close(connection) + end :ok end From 164ffbcab822eda4c28f912082b6a7a3ec64a7e5 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 17 Sep 2024 13:17:40 -0400 Subject: [PATCH 14/28] Fix return value when not doing STARTTLS --- lib/pleroma/ldap.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/ldap.ex b/lib/pleroma/ldap.ex index 3df20fe094..6be2188f43 100644 --- a/lib/pleroma/ldap.ex +++ b/lib/pleroma/ldap.ex @@ -144,7 +144,7 @@ defp connect do end true -> - {:ok, :connection} + {:ok, connection} end after :ok From a1972d57e30f41e5173d61c0d0936685738c560d Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 17 Sep 2024 13:19:54 -0400 Subject: [PATCH 15/28] Link the eldap connection process Ensure if LDAP GenServer crashes it gets cleaned up, and we should crash and restart if somehow the eldap connection process crashes unexpectedly as we can't seem to receive any DOWN messages from it, etc. --- lib/pleroma/ldap.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pleroma/ldap.ex b/lib/pleroma/ldap.ex index 6be2188f43..0723cd0941 100644 --- a/lib/pleroma/ldap.ex +++ b/lib/pleroma/ldap.ex @@ -59,6 +59,7 @@ defp do_handle_connect do case connect() do {:ok, connection} -> :eldap.controlling_process(connection, self()) + Process.link(connection) [connection: connection] _ -> From 14a9663f1abe49b8f4f4f719fa2f4db3a5dd81b7 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 17 Sep 2024 13:28:42 -0400 Subject: [PATCH 16/28] Remove cacertfile as child of SSL and TLS options We need to pass the cacerts (list of charlist encoded certs) not cacertfile, so our new cacertfile setting handles this for us. --- config/description.exs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/config/description.exs b/config/description.exs index ade47b7e02..5062842f04 100644 --- a/config/description.exs +++ b/config/description.exs @@ -2241,14 +2241,8 @@ label: "SSL options", type: :keyword, description: "Additional SSL options", - suggestions: [cacertfile: "path/to/file/with/PEM/cacerts", verify: :verify_peer], + suggestions: [verify: :verify_peer], children: [ - %{ - key: :cacertfile, - type: :string, - description: "Path to file with PEM encoded cacerts", - suggestions: ["path/to/file/with/PEM/cacerts"] - }, %{ key: :verify, type: :atom, @@ -2268,14 +2262,8 @@ label: "TLS options", type: :keyword, description: "Additional TLS options", - suggestions: [cacertfile: "path/to/file/with/PEM/cacerts", verify: :verify_peer], + suggestions: [verify: :verify_peer], children: [ - %{ - key: :cacertfile, - type: :string, - description: "Path to file with PEM encoded cacerts", - suggestions: ["path/to/file/with/PEM/cacerts"] - }, %{ key: :verify, type: :atom, From 363b462c54c454e847072869db09f8f4d5da4426 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 17 Sep 2024 13:36:46 -0400 Subject: [PATCH 17/28] Make the email attribute configurable While here, fix the System.get_env usage to use the normal fallback value method and improve the UID label description --- config/config.exs | 11 ++++++----- config/description.exs | 9 ++++++++- lib/pleroma/ldap.ex | 4 +++- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/config/config.exs b/config/config.exs index f53a083d04..47ddfac5ac 100644 --- a/config/config.exs +++ b/config/config.exs @@ -612,16 +612,17 @@ config :pleroma, :ldap, enabled: System.get_env("LDAP_ENABLED") == "true", - host: System.get_env("LDAP_HOST") || "localhost", - port: String.to_integer(System.get_env("LDAP_PORT") || "389"), + host: System.get_env("LDAP_HOST", "localhost"), + port: String.to_integer(System.get_env("LDAP_PORT", "389")), ssl: System.get_env("LDAP_SSL") == "true", sslopts: [], tls: System.get_env("LDAP_TLS") == "true", tlsopts: [], - base: System.get_env("LDAP_BASE") || "dc=example,dc=com", - uid: System.get_env("LDAP_UID") || "cn", + base: System.get_env("LDAP_BASE", "dc=example,dc=com"), + uid: System.get_env("LDAP_UID", "cn"), # defaults to CAStore's Mozilla roots - cacertfile: nil + cacertfile: System.get_env("LDAP_CACERTFILE", nil), + mail: System.get_env("LDAP_MAIL", "mail") oauth_consumer_strategies = System.get_env("OAUTH_CONSUMER_STRATEGIES") diff --git a/config/description.exs b/config/description.exs index 5062842f04..e85ec0ff80 100644 --- a/config/description.exs +++ b/config/description.exs @@ -2280,7 +2280,7 @@ }, %{ key: :uid, - label: "UID", + label: "UID Attribute", type: :string, description: "LDAP attribute name to authenticate the user, e.g. when \"cn\", the filter will be \"cn=username,base\"", @@ -2291,6 +2291,13 @@ label: "CACertfile", type: :string, description: "Path to CA certificate file" + }, + %{ + key: :mail, + label: "Mail Attribute", + type: :string, + description: "LDAP attribute name to use as the email address when automatically registering the user on first login", + suggestions: ["mail"] } ] }, diff --git a/lib/pleroma/ldap.ex b/lib/pleroma/ldap.ex index 0723cd0941..8e9c591b29 100644 --- a/lib/pleroma/ldap.ex +++ b/lib/pleroma/ldap.ex @@ -205,6 +205,8 @@ defp register_user(connection, base, uid, name) do end defp try_register(name, attributes) do + mail_attribute = Config.get([:ldap, :mail]) + params = %{ name: name, nickname: name, @@ -212,7 +214,7 @@ defp try_register(name, attributes) do } params = - case List.keyfind(attributes, ~c"mail", 0) do + case List.keyfind(attributes, to_charlist(mail_attribute), 0) do {_, [mail]} -> Map.put_new(params, :email, :erlang.list_to_binary(mail)) _ -> params end From 21bf229731f27426564140650397e51ba4bb4b93 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 17 Sep 2024 13:43:21 -0400 Subject: [PATCH 18/28] Reduce LDAP timeouts 10 seconds is way too long for any login attempt or search result. LDAP should always be fast. --- lib/pleroma/ldap.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/ldap.ex b/lib/pleroma/ldap.ex index 8e9c591b29..33c9cff295 100644 --- a/lib/pleroma/ldap.ex +++ b/lib/pleroma/ldap.ex @@ -8,8 +8,8 @@ defmodule Pleroma.LDAP do import Pleroma.Web.Auth.Helpers, only: [fetch_user: 1] - @connection_timeout 10_000 - @search_timeout 10_000 + @connection_timeout 2_000 + @search_timeout 2_000 def start_link(_) do GenServer.start_link(__MODULE__, [], name: __MODULE__) From 1d123832da6a2b8c67f34006b4ea05e0be86e366 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 17 Sep 2024 13:46:49 -0400 Subject: [PATCH 19/28] Formatting --- config/description.exs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/description.exs b/config/description.exs index e85ec0ff80..47f4771eb8 100644 --- a/config/description.exs +++ b/config/description.exs @@ -2296,7 +2296,8 @@ key: :mail, label: "Mail Attribute", type: :string, - description: "LDAP attribute name to use as the email address when automatically registering the user on first login", + description: + "LDAP attribute name to use as the email address when automatically registering the user on first login", suggestions: ["mail"] } ] From ea63533cf28be8218a27806b1f6430f0c1ca4a01 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 17 Sep 2024 13:46:56 -0400 Subject: [PATCH 20/28] Change :connection to :handle to match upstream nomenclature --- lib/pleroma/ldap.ex | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/pleroma/ldap.ex b/lib/pleroma/ldap.ex index 33c9cff295..b357b371ad 100644 --- a/lib/pleroma/ldap.ex +++ b/lib/pleroma/ldap.ex @@ -47,7 +47,7 @@ def handle_continue(:connect, _state), do: do_handle_connect() def handle_info(:connect, _state), do: do_handle_connect() def handle_info({:bind_after_reconnect, name, password, from}, state) do - result = bind_user(state[:connection], name, password) + result = bind_user(state[:handle], name, password) GenServer.reply(from, result) @@ -57,10 +57,10 @@ def handle_info({:bind_after_reconnect, name, password, from}, state) do defp do_handle_connect do state = case connect() do - {:ok, connection} -> - :eldap.controlling_process(connection, self()) - Process.link(connection) - [connection: connection] + {:ok, handle} -> + :eldap.controlling_process(handle, self()) + Process.link(handle) + [handle: handle] _ -> Logger.error("Failed to connect to LDAP. Retrying in 5000ms") @@ -73,7 +73,7 @@ defp do_handle_connect do @impl true def handle_call({:bind_user, name, password}, from, state) do - case bind_user(state[:connection], name, password) do + case bind_user(state[:handle], name, password) do :needs_reconnect -> Process.send(self(), {:bind_after_reconnect, name, password, from}, []) {:noreply, state, {:continue, :connect}} @@ -85,10 +85,10 @@ def handle_call({:bind_user, name, password}, from, state) do @impl true def terminate(_, state) do - connection = Keyword.get(state, :connection) + handle = Keyword.get(state, :handle) - if not is_nil(connection) do - :eldap.close(connection) + if not is_nil(handle) do + :eldap.close(handle) end :ok @@ -127,25 +127,25 @@ defp connect do end case :eldap.open([to_charlist(host)], options) do - {:ok, connection} -> + {:ok, handle} -> try do cond do tls -> case :eldap.start_tls( - connection, + handle, tlsopts, @connection_timeout ) do :ok -> - {:ok, connection} + {:ok, handle} error -> Logger.error("Could not start TLS: #{inspect(error)}") - :eldap.close(connection) + :eldap.close(handle) end true -> - {:ok, connection} + {:ok, handle} end after :ok @@ -157,24 +157,24 @@ defp connect do end end - defp bind_user(connection, name, password) do + defp bind_user(handle, name, password) do uid = Config.get([:ldap, :uid], "cn") base = Config.get([:ldap, :base]) - case :eldap.simple_bind(connection, "#{uid}=#{name},#{base}", password) do + case :eldap.simple_bind(handle, "#{uid}=#{name},#{base}", password) do :ok -> case fetch_user(name) do %User{} = user -> user _ -> - register_user(connection, base, uid, name) + register_user(handle, base, uid, name) end # eldap does not inform us of socket closure # until it is used {:error, {:gen_tcp_error, :closed}} -> - :eldap.close(connection) + :eldap.close(handle) :needs_reconnect error -> @@ -183,8 +183,8 @@ defp bind_user(connection, name, password) do end end - defp register_user(connection, base, uid, name) do - case :eldap.search(connection, [ + defp register_user(handle, base, uid, name) do + case :eldap.search(handle, [ {:base, to_charlist(base)}, {:filter, :eldap.equalityMatch(to_charlist(uid), to_charlist(name))}, {:scope, :eldap.wholeSubtree()}, From 2b482e34ebf5aee49350d8198e6fade8820b3834 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 17 Sep 2024 13:54:57 -0400 Subject: [PATCH 21/28] Improve matching on bind errors --- lib/pleroma/ldap.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/ldap.ex b/lib/pleroma/ldap.ex index b357b371ad..cd84dee023 100644 --- a/lib/pleroma/ldap.ex +++ b/lib/pleroma/ldap.ex @@ -177,9 +177,9 @@ defp bind_user(handle, name, password) do :eldap.close(handle) :needs_reconnect - error -> + {:error, error} = e -> Logger.error("Could not bind LDAP user #{name}: #{inspect(error)}") - {:error, {:ldap_bind_error, error}} + e end end From 35ddb1d2c8a53dcb54178522811242ef40a63211 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 17 Sep 2024 13:57:10 -0400 Subject: [PATCH 22/28] LDAP genserver changelog --- changelog.d/ldap-refactor.change | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/ldap-refactor.change diff --git a/changelog.d/ldap-refactor.change b/changelog.d/ldap-refactor.change new file mode 100644 index 0000000000..1510eea6aa --- /dev/null +++ b/changelog.d/ldap-refactor.change @@ -0,0 +1 @@ +LDAP authentication has been refactored to operate as a GenServer process which will maintain an active connection to the LDAP server. From 1de5208a9e90485be38ac0d00088f18c5b36390a Mon Sep 17 00:00:00 2001 From: Mint Date: Tue, 17 Sep 2024 21:48:37 +0300 Subject: [PATCH 23/28] Cheatsheet: add Mua mail adapter config --- docs/configuration/cheatsheet.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index 0b4e53b6f5..ba41fe84df 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -742,6 +742,21 @@ config :pleroma, Pleroma.Emails.Mailer, auth: :always ``` +An example for Mua adapter: + +```elixir +config :pleroma, Pleroma.Emails.Mailer, + enabled: true, + adapter: Swoosh.Adapters.Mua, + relay: "mail.example.com", + port: 465, + auth: [ + username: "YOUR_USERNAME@domain.tld", + password: "YOUR_SMTP_PASSWORD" + ], + protocol: :ssl +``` + ### :email_notifications Email notifications settings. From 73204c1bca740dbca5c780891fc720ac728c11a6 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 18 Sep 2024 11:16:16 -0400 Subject: [PATCH 24/28] LDAP: fix compile warning Sometimes the compile will emit the following warning, so we'll just avoid it by making it call a function in the LDAP module which will never have this problem. warning: :GenServer.call/2 is undefined (module :GenServer is not available or is yet to be defined) --- changelog.d/ldap-warning.skip | 0 lib/pleroma/ldap.ex | 4 ++++ lib/pleroma/web/auth/ldap_authenticator.ex | 3 ++- 3 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 changelog.d/ldap-warning.skip diff --git a/changelog.d/ldap-warning.skip b/changelog.d/ldap-warning.skip new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lib/pleroma/ldap.ex b/lib/pleroma/ldap.ex index cd84dee023..46a2d0c17b 100644 --- a/lib/pleroma/ldap.ex +++ b/lib/pleroma/ldap.ex @@ -94,6 +94,10 @@ def terminate(_, state) do :ok end + def bind_user(name, password) do + GenServer.call(__MODULE__, {:bind_user, name, password}) + end + defp connect do ldap = Config.get(:ldap, []) host = Keyword.get(ldap, :host, "localhost") diff --git a/lib/pleroma/web/auth/ldap_authenticator.ex b/lib/pleroma/web/auth/ldap_authenticator.ex index c420c8bc30..7eb06183d3 100644 --- a/lib/pleroma/web/auth/ldap_authenticator.ex +++ b/lib/pleroma/web/auth/ldap_authenticator.ex @@ -3,6 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Auth.LDAPAuthenticator do + alias Pleroma.LDAP alias Pleroma.User import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1] @@ -19,7 +20,7 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do def get_user(%Plug.Conn{} = conn) do with {:ldap, true} <- {:ldap, Pleroma.Config.get([:ldap, :enabled])}, {:ok, {name, password}} <- fetch_credentials(conn), - %User{} = user <- GenServer.call(Pleroma.LDAP, {:bind_user, name, password}) do + %User{} = user <- LDAP.bind_user(name, password) do {:ok, user} else {:ldap, _} -> From ecd1b8393befe91175872af3db67a5c01f10eaf2 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 18 Sep 2024 12:07:52 -0400 Subject: [PATCH 25/28] Oban: update to 2.18.3 This release includes the fix which should prevent the scenario where Postgrex crashes can cause Oban to get into a state where it will stop processing jobs. --- changelog.d/oban-update.change | 1 + mix.lock | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog.d/oban-update.change diff --git a/changelog.d/oban-update.change b/changelog.d/oban-update.change new file mode 100644 index 0000000000..48a54ed2d2 --- /dev/null +++ b/changelog.d/oban-update.change @@ -0,0 +1 @@ +Oban updated to 2.18.3 diff --git a/mix.lock b/mix.lock index 2cf44862b4..421f99ec04 100644 --- a/mix.lock +++ b/mix.lock @@ -92,7 +92,7 @@ "nimble_parsec": {:hex, :nimble_parsec, "0.6.0", "32111b3bf39137144abd7ba1cce0914533b2d16ef35e8abc5ec8be6122944263", [:mix], [], "hexpm", "27eac315a94909d4dc68bc07a4a83e06c8379237c5ea528a9acff4ca1c873c52"}, "nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"}, "nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]}, - "oban": {:hex, :oban, "2.18.2", "583e78965ee15263ac968e38c983bad169ae55eadaa8e1e39912562badff93ba", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9dd25fd35883a91ed995e9fe516e479344d3a8623dfe2b8c3fc8e5be0228ec3a"}, + "oban": {:hex, :oban, "2.18.3", "1608c04f8856c108555c379f2f56bc0759149d35fa9d3b825cb8a6769f8ae926", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "36ca6ca84ef6518f9c2c759ea88efd438a3c81d667ba23b02b062a0aa785475e"}, "oban_live_dashboard": {:hex, :oban_live_dashboard, "0.1.1", "8aa4ceaf381c818f7d5c8185cc59942b8ac82ef0cf559881aacf8d3f8ac7bdd3", [:mix], [{:oban, "~> 2.15", [hex: :oban, repo: "hexpm", optional: false]}, {:phoenix_live_dashboard, "~> 0.7", [hex: :phoenix_live_dashboard, repo: "hexpm", optional: false]}], "hexpm", "16dc4ce9c9a95aa2e655e35ed4e675652994a8def61731a18af85e230e1caa63"}, "octo_fetch": {:hex, :octo_fetch, "0.4.0", "074b5ecbc08be10b05b27e9db08bc20a3060142769436242702931c418695b19", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "cf8be6f40cd519d7000bb4e84adcf661c32e59369ca2827c4e20042eda7a7fc6"}, "open_api_spex": {:hex, :open_api_spex, "3.18.2", "8c855e83bfe8bf81603d919d6e892541eafece3720f34d1700b58024dadde247", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:ymlr, "~> 2.0 or ~> 3.0 or ~> 4.0", [hex: :ymlr, repo: "hexpm", optional: true]}], "hexpm", "aa3e6dcfc0ad6a02596b2172662da21c9dd848dac145ea9e603f54e3d81b8d2b"}, From f00545d85bd601734cdbbc28454f33541dbf530d Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 18 Sep 2024 13:14:17 -0400 Subject: [PATCH 26/28] Elixir 1.14 and Erlang/OTP 23 is now the minimum supported release --- .gitlab-ci.yml | 8 ++++---- changelog.d/elixir.change | 1 + docs/installation/debian_based_jp.md | 2 +- docs/installation/generic_dependencies.include | 4 ++-- mix.exs | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) create mode 100644 changelog.d/elixir.change diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 76d1a4210d..39947c75e6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,8 +1,8 @@ -image: git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.13.4-otp-25 +image: git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.14.5-otp-25 variables: &global_variables # Only used for the release - ELIXIR_VER: 1.13.4 + ELIXIR_VER: 1.14.5 POSTGRES_DB: pleroma_test POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres @@ -71,7 +71,7 @@ check-changelog: tags: - amd64 -build-1.13.4-otp-25: +build-1.14.5-otp-25: extends: - .build_changes_policy - .using-ci-base @@ -119,7 +119,7 @@ benchmark: - mix ecto.migrate - mix pleroma.load_testing -unit-testing-1.13.4-otp-25: +unit-testing-1.14.5-otp-25: extends: - .build_changes_policy - .using-ci-base diff --git a/changelog.d/elixir.change b/changelog.d/elixir.change new file mode 100644 index 0000000000..779c01562b --- /dev/null +++ b/changelog.d/elixir.change @@ -0,0 +1 @@ +Elixir 1.14 and Erlang/OTP 23 is now the minimum supported release diff --git a/docs/installation/debian_based_jp.md b/docs/installation/debian_based_jp.md index 5a0823a634..0817934fff 100644 --- a/docs/installation/debian_based_jp.md +++ b/docs/installation/debian_based_jp.md @@ -14,7 +14,7 @@ Note: This article is potentially outdated because at this time we may not have - PostgreSQL 11.0以上 (Ubuntu16.04では9.5しか提供されていないので,[](https://www.postgresql.org/download/linux/ubuntu/)こちらから新しいバージョンを入手してください) - `postgresql-contrib` 11.0以上 (同上) -- Elixir 1.13 以上 ([Debianのリポジトリからインストールしないこと!!! ここからインストールすること!](https://elixir-lang.org/install.html#unix-and-unix-like)。または [asdf](https://github.com/asdf-vm/asdf) をpleromaユーザーでインストールしてください) +- Elixir 1.14 以上 ([Debianのリポジトリからインストールしないこと!!! ここからインストールすること!](https://elixir-lang.org/install.html#unix-and-unix-like)。または [asdf](https://github.com/asdf-vm/asdf) をpleromaユーザーでインストールしてください) - `erlang-dev` - `erlang-nox` - `git` diff --git a/docs/installation/generic_dependencies.include b/docs/installation/generic_dependencies.include index bdb7f94d3a..9f07f62c6c 100644 --- a/docs/installation/generic_dependencies.include +++ b/docs/installation/generic_dependencies.include @@ -1,8 +1,8 @@ ## Required dependencies * PostgreSQL >=11.0 -* Elixir >=1.13.0 <1.17 -* Erlang OTP >=22.2.0 (supported: <27) +* Elixir >=1.14.0 <1.17 +* Erlang OTP >=23.0.0 (supported: <27) * git * file / libmagic * gcc or clang diff --git a/mix.exs b/mix.exs index ceae5c26df..89ec5e831e 100644 --- a/mix.exs +++ b/mix.exs @@ -5,7 +5,7 @@ def project do [ app: :pleroma, version: version("2.7.0"), - elixir: "~> 1.13", + elixir: "~> 1.14", elixirc_paths: elixirc_paths(Mix.env()), compilers: Mix.compilers(), elixirc_options: [warnings_as_errors: warnings_as_errors(), prune_code_paths: false], From 7e303600fb2914ab66fe54a8022ddc65ff93edf5 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 18 Sep 2024 17:15:55 +0000 Subject: [PATCH 27/28] Remove old elixir 1.12 build image generation script --- ci/elixir-1.12/Dockerfile | 8 -------- ci/elixir-1.12/build_and_push.sh | 1 - 2 files changed, 9 deletions(-) delete mode 100644 ci/elixir-1.12/Dockerfile delete mode 100755 ci/elixir-1.12/build_and_push.sh diff --git a/ci/elixir-1.12/Dockerfile b/ci/elixir-1.12/Dockerfile deleted file mode 100644 index a2b5668730..0000000000 --- a/ci/elixir-1.12/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM elixir:1.12.3 - -# Single RUN statement, otherwise intermediate images are created -# https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run -RUN apt-get update &&\ - apt-get install -y libmagic-dev cmake libimage-exiftool-perl ffmpeg &&\ - mix local.hex --force &&\ - mix local.rebar --force diff --git a/ci/elixir-1.12/build_and_push.sh b/ci/elixir-1.12/build_and_push.sh deleted file mode 100755 index 508262ed82..0000000000 --- a/ci/elixir-1.12/build_and_push.sh +++ /dev/null @@ -1 +0,0 @@ -docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.12 --push . From 1bd28e7d592b429c5eee072db8d1f2ae77d76e29 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 18 Sep 2024 17:28:48 +0000 Subject: [PATCH 28/28] CI script to build and publish an image for Elixir 1.14 --- ci/{elixir-1.13.4-otp-25 => elixir-1.14.5-otp-25}/Dockerfile | 2 +- .../build_and_push.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename ci/{elixir-1.13.4-otp-25 => elixir-1.14.5-otp-25}/Dockerfile (91%) rename ci/{elixir-1.13.4-otp-25 => elixir-1.14.5-otp-25}/build_and_push.sh (52%) diff --git a/ci/elixir-1.13.4-otp-25/Dockerfile b/ci/elixir-1.14.5-otp-25/Dockerfile similarity index 91% rename from ci/elixir-1.13.4-otp-25/Dockerfile rename to ci/elixir-1.14.5-otp-25/Dockerfile index 25a1639e89..3a35c84c39 100644 --- a/ci/elixir-1.13.4-otp-25/Dockerfile +++ b/ci/elixir-1.14.5-otp-25/Dockerfile @@ -1,4 +1,4 @@ -FROM elixir:1.13.4-otp-25 +FROM elixir:1.14.5-otp-25 # Single RUN statement, otherwise intermediate images are created # https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run diff --git a/ci/elixir-1.13.4-otp-25/build_and_push.sh b/ci/elixir-1.14.5-otp-25/build_and_push.sh similarity index 52% rename from ci/elixir-1.13.4-otp-25/build_and_push.sh rename to ci/elixir-1.14.5-otp-25/build_and_push.sh index b8ca1d24d7..912c47d0c4 100755 --- a/ci/elixir-1.13.4-otp-25/build_and_push.sh +++ b/ci/elixir-1.14.5-otp-25/build_and_push.sh @@ -1 +1 @@ -docker buildx build --platform linux/amd64,linux/arm64 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.13.4-otp-25 --push . +docker buildx build --platform linux/amd64,linux/arm64 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.14.5-otp-25 --push .