Support `sslmode` in `DATABASE_URL` in CE (#5280)

* sslmode

* Update config/runtime.exs

---------

Co-authored-by: Cenk Kücük <cenk@plausible.io>
This commit is contained in:
ruslandoga 2025-05-20 16:21:48 +03:00 committed by GitHub
parent 07fbfb0c05
commit 97449613e1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 159 additions and 9 deletions

View File

@ -476,13 +476,48 @@ if db_socket_dir? do
password: password password: password
end end
else else
config :plausible, Plausible.Repo, config :plausible, Plausible.Repo, url: db_url
url: db_url,
socket_options: db_maybe_ipv6
if db_cacertfile do unless Enum.empty?(db_maybe_ipv6) do
config :plausible, Plausible.Repo, ssl: [cacertfile: db_cacertfile] config :plausible, Plausible.Repo, socket_options: db_maybe_ipv6
end end
db_query = URI.decode_query(db_uri.query || "")
# https://www.postgresql.org/docs/current/libpq-ssl.html#LIBPQ-SSL-SSLMODE-STATEMENTS
pg_sslmode = db_query["sslmode"]
pg_ssl =
cond do
db_cacertfile ->
[cacertfile: db_cacertfile, verify: :verify_peer]
pg_sslmode == "verify-full" ->
if pg_sslrootcert = db_query["sslrootcert"] do
[cacertfile: pg_sslrootcert, verify: :verify_peer]
else
raise ArgumentError,
"PostgreSQL SSL mode `sslmode=#{pg_sslmode}` requires a certificate, set it in `sslrootcert`"
end
pg_sslmode == "verify-ca" ->
[cacerts: :public_key.cacerts_get(), verify: :verify_peer]
pg_sslmode == "require" ->
[verify: :verify_none]
pg_sslmode == "disable" ->
false
pg_sslmode ->
raise ArgumentError,
"PostgreSQL SSL mode `sslmode=#{pg_sslmode}` is not supported, use `disable`, `require`, `verify-ca` or `verify-full` instead"
true ->
# tls is disabled by default, because in self-hosted docker compose postgres is co-located
false
end
config :plausible, Plausible.Repo, ssl: pg_ssl
end end
sentry_app_version = runtime_metadata[:version] || app_version sentry_app_version = runtime_metadata[:version] || app_version

View File

@ -458,7 +458,7 @@ defmodule Plausible.ConfigTest do
assert get_in(config, [:plausible, Plausible.Repo]) == [ assert get_in(config, [:plausible, Plausible.Repo]) == [
url: "postgres://postgres:postgres@plausible_db:5432/plausible_db", url: "postgres://postgres:postgres@plausible_db:5432/plausible_db",
socket_options: [] ssl: false
] ]
end end
@ -500,7 +500,7 @@ defmodule Plausible.ConfigTest do
assert get_in(config, [:plausible, Plausible.Repo]) == [ assert get_in(config, [:plausible, Plausible.Repo]) == [
url: url:
"postgresql://your_username:your_password@cluster-do-user-1234567-0.db.ondigitalocean.com:25060/defaultdb", "postgresql://your_username:your_password@cluster-do-user-1234567-0.db.ondigitalocean.com:25060/defaultdb",
socket_options: [] ssl: false
] ]
end end
@ -516,8 +516,123 @@ defmodule Plausible.ConfigTest do
assert get_in(config, [:plausible, Plausible.Repo]) == [ assert get_in(config, [:plausible, Plausible.Repo]) == [
url: url:
"postgresql://your_username:your_password@cluster-do-user-1234567-0.db.ondigitalocean.com:25060/defaultdb", "postgresql://your_username:your_password@cluster-do-user-1234567-0.db.ondigitalocean.com:25060/defaultdb",
socket_options: [], ssl: [cacertfile: "/path/to/cacert.pem", verify: :verify_peer]
ssl: [cacertfile: "/path/to/cacert.pem"] ]
end
test "sslmode=require disables peer verification" do
env = [
{"DATABASE_URL",
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslmode=require"}
]
config = runtime_config(env)
assert get_in(config, [:plausible, Plausible.Repo]) == [
url:
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslmode=require",
ssl: [verify: :verify_none]
]
end
test "sslmode=disable explicitly disables SSL" do
env = [
{"DATABASE_URL",
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslmode=disable"}
]
config = runtime_config(env)
assert get_in(config, [:plausible, Plausible.Repo]) == [
url:
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslmode=disable",
ssl: false
]
end
test "sslmode=verify-ca uses system certificates" do
env = [
{"DATABASE_URL",
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslmode=verify-ca"}
]
config = runtime_config(env)
assert get_in(config, [:plausible, Plausible.Repo]) == [
url:
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslmode=verify-ca",
ssl: [
cacerts: :public_key.cacerts_get(),
verify: :verify_peer
]
]
end
test "sslmode=verify-full raises error without certificate" do
env = [
{"DATABASE_URL",
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslmode=verify-full"}
]
assert_raise ArgumentError,
~r/PostgreSQL SSL mode `sslmode=verify-full` requires a certificate/,
fn -> runtime_config(env) end
end
test "unsupported sslmode raises error" do
env = [
{"DATABASE_URL",
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslmode=prefer"}
]
assert_raise ArgumentError, ~r/PostgreSQL SSL mode `sslmode=prefer` is not supported/, fn ->
runtime_config(env)
end
end
test "verify-full with sslrootcert enables peer verification" do
env = [
{"DATABASE_URL",
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslmode=verify-full&sslrootcert=/path/to/cert.pem"}
]
config = runtime_config(env)
assert get_in(config, [:plausible, Plausible.Repo]) == [
url:
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslmode=verify-full&sslrootcert=/path/to/cert.pem",
ssl: [cacertfile: "/path/to/cert.pem", verify: :verify_peer]
]
end
test "sslrootcert alone is not enough for peer verification" do
env = [
{"DATABASE_URL",
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslrootcert=/path/to/cert.pem"}
]
config = runtime_config(env)
assert get_in(config, [:plausible, Plausible.Repo]) == [
url:
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslrootcert=/path/to/cert.pem",
ssl: false
]
end
test "DATABASE_CACERTFILE takes precedence over sslrootcert" do
env = [
{"DATABASE_URL",
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslrootcert=/url/cert.pem"},
{"DATABASE_CACERTFILE", "/env/cacert.pem"}
]
config = runtime_config(env)
assert get_in(config, [:plausible, Plausible.Repo]) == [
url:
"postgresql://username:password@company.postgres.database.azure.com:5432/prod_plausible?sslrootcert=/url/cert.pem",
ssl: [cacertfile: "/env/cacert.pem", verify: :verify_peer]
] ]
end end
end end