Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't require TOTP vault key in CE #4317

Merged
merged 1 commit into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ All notable changes to this project will be documented in this file.
- Make TCP connections try IPv6 first with IPv4 fallback in CE plausible/analytics#4245
- `is` and `is not` filters in dashboard no longer support wildcards. Use contains/does not contain filter instead.
- `bounce_rate` metric now returns 0 instead of null for event:page breakdown when page has never been entry page.
- Make `TOTP_VAULT_KEY` optional plausible/analytics#4317

### Fixed

Expand Down
36 changes: 23 additions & 13 deletions config/runtime.exs
Original file line number Diff line number Diff line change
Expand Up @@ -153,19 +153,31 @@ end

# Can be generated with `Base.encode64(:crypto.strong_rand_bytes(32))` from
# iex shell or `openssl rand -base64 32` from command line.
totp_vault_key = get_var_from_path_or_env(config_dir, "TOTP_VAULT_KEY", nil)

case totp_vault_key do
nil ->
raise "TOTP_VAULT_KEY configuration option is required. See https://github.com/plausible/community-edition/tree/v2.1.0?tab=readme-ov-file#quick-start"

key ->
if byte_size(Base.decode64!(key)) != 32 do
raise "TOTP_VAULT_KEY must exactly 32 bytes long. See https://github.com/plausible/community-edition/tree/v2.1.0?tab=readme-ov-file#quick-start"
totp_vault_key =
if totp_vault_key_base64 = get_var_from_path_or_env(config_dir, "TOTP_VAULT_KEY") do
case Base.decode64(totp_vault_key_base64) do
{:ok, totp_vault_key} ->
if byte_size(totp_vault_key) == 32 do
totp_vault_key
else
raise ArgumentError, """
TOTP_VAULT_KEY must be Base64 encoded 32 bytes, e.g. `openssl rand -base64 32`.
Got Base64 encoded #{byte_size(totp_vault_key)} bytes.
More info: https://github.com/plausible/community-edition/tree/v2.1.1#quick-start
"""
end

:error ->
raise ArgumentError, """
TOTP_VAULT_KEY must be Base64 encoded 32 bytes, e.g. `openssl rand -base64 32`
More info: https://github.com/plausible/community-edition/tree/v2.1.1#quick-start
"""
end
end
else
Plug.Crypto.KeyGenerator.generate(secret_key_base, "totp", length: 32, iterations: 100_000)
end

### Mandatory params End
config :plausible, Plausible.Auth.TOTP, vault_key: totp_vault_key

build_metadata_raw = get_var_from_path_or_env(config_dir, "BUILD_METADATA", "{}")

Expand Down Expand Up @@ -197,8 +209,6 @@ runtime_metadata = [

config :plausible, :runtime_metadata, runtime_metadata

config :plausible, Plausible.Auth.TOTP, vault_key: totp_vault_key

sentry_dsn = get_var_from_path_or_env(config_dir, "SENTRY_DSN")
honeycomb_api_key = get_var_from_path_or_env(config_dir, "HONEYCOMB_API_KEY")
honeycomb_dataset = get_var_from_path_or_env(config_dir, "HONEYCOMB_DATASET")
Expand Down
1 change: 0 additions & 1 deletion lib/plausible/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ defmodule Plausible.Application do
:plausible
|> Application.fetch_env!(Plausible.Auth.TOTP)
|> Keyword.fetch!(:vault_key)
|> Base.decode64!()
end

defp finch_pool_config() do
Expand Down
54 changes: 54 additions & 0 deletions test/plausible/config_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,60 @@ defmodule Plausible.ConfigTest do
end
end

describe "totp" do
test "pbkdf2 if not set" do
env = [
{"TOTP_VAULT_KEY", nil}
]

config = runtime_config(env)

assert [vault_key: vault_key] = get_in(config, [:plausible, Plausible.Auth.TOTP])
assert byte_size(vault_key) == 32

# make sure it doesn't change between releases
assert vault_key ==
"\x95\x9C\x05\x9A\xCD\xE4\xEF\xDDH\xFB\xCA\xD5o\xD1z\xCCTǐ\xBC\"J\xF8:\xFAs\xCA\x0Fo\x10\x9B\x84"
end

test "can be Base64-encoded 32 bytes (with padding)" do
# $ openssl rand -base64 32
# dx2W6PNd/QIC6IyYVWMEaG2fI8/5WVylryM3mRaOpAo=
env = [
{"TOTP_VAULT_KEY", "dx2W6PNd/QIC6IyYVWMEaG2fI8/5WVylryM3mRaOpAo="}
]

config = runtime_config(env)

assert [vault_key: vault_key] = get_in(config, [:plausible, Plausible.Auth.TOTP])
assert byte_size(vault_key) == 32
assert vault_key == Base.decode64!("dx2W6PNd/QIC6IyYVWMEaG2fI8/5WVylryM3mRaOpAo=")
end

test "fails on invalid key length" do
assert_raise ArgumentError, ~r/Got Base64 encoded 31 bytes/, fn ->
runtime_config(_env = [{"TOTP_VAULT_KEY", Base.encode64(:crypto.strong_rand_bytes(31))}])
end

assert_raise ArgumentError, ~r/Got Base64 encoded 33 bytes/, fn ->
runtime_config(_env = [{"TOTP_VAULT_KEY", Base.encode64(:crypto.strong_rand_bytes(33))}])
end
end

test "fails on invalid encoding" do
assert_raise ArgumentError,
~r/TOTP_VAULT_KEY must be Base64 encoded/,
fn ->
runtime_config(
_env = [
{"TOTP_VAULT_KEY",
"openssl" <> Base.encode64(:crypto.strong_rand_bytes(32))}
]
)
end
end
end

defp runtime_config(env) do
put_system_env_undo(env)
Config.Reader.read!("config/runtime.exs", env: :prod)
Expand Down