commit: e24e107f3d3ae6024068c6509a7cb0582ea4bf02
parent e59706c201bd71525c0a15008c3cb5dcdfb73289
Author: Mark Felder <feld@feld.me>
Date: Mon, 16 Sep 2024 11:51:22 -0400
Merge remote-tracking branch 'origin/develop' into retry-tests
Diffstat:
6 files changed, 61 insertions(+), 32 deletions(-)
diff --git a/changelog.d/ldap-ca.add 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/changelog.d/ldaps.fix b/changelog.d/ldaps.fix
@@ -0,0 +1 @@
+LDAPS connections (implicit TLS) are now supported.
diff --git a/config/config.exs b/config/config.exs
@@ -619,7 +619,9 @@ config :pleroma, :ldap,
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
@@ -968,12 +968,13 @@ 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"
+* `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
@@ -40,39 +40,52 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator 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()
+
+ 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 =
- [{:port, port}, {:ssl, ssl}, {:timeout, @connection_timeout}] ++
- if sslopts != [], do: [{:sslopts, sslopts}], else: []
+ 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
- :application.ensure_all_started(:ssl)
-
- case :eldap.start_tls(
- connection,
- Keyword.merge(
- [
- verify: :verify_peer,
- cacerts: :certifi.cacerts(),
- customize_hostname_check: [
- fqdn_fun: fn _ -> to_charlist(host) end
- ]
- ],
- tlsopts
- ),
- @connection_timeout
- ) do
- :ok ->
- :ok
-
- error ->
- Logger.error("Could not start TLS: #{inspect(error)}")
- end
+ 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)
@@ -147,4 +160,16 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator 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
@@ -204,7 +204,6 @@ defmodule Pleroma.Mixfile 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},