From 63fabcbb5f1da302fa13ce9e4017dea56cccee85 Mon Sep 17 00:00:00 2001 From: hq1 Date: Mon, 21 Aug 2023 11:47:11 +0200 Subject: [PATCH] Reconfigure session cookie (#3274) * Reconfigure session cookie * Allow configure secure cookie (#3277) * Update config/runtime.exs Co-authored-by: ruslandoga * fix runtime conf * Revert "fix runtime conf" This reverts commit ff37e479e4447c808a7a28118ddc6d5c33880928. * Revert "Update config/runtime.exs" This reverts commit 5d9b310b02daeaff7a9c217867c8e319ae5c03d6. * Revert "Allow configure secure cookie (#3277)" This reverts commit 7401a2ad3f9c93b0d7f7032f7c9c263a6fd4395d. * Read SECURE_COOKIE with defaults per cloud/selfhost * Include environment in cookie name * Remove redundant option * Format * s/Map.replace/Map.put * up * One more try * Prevent browser refreshes on socket connection error We'll keep the log and don't make the page look dumb even if there's some misconfiguration going on. --------- Co-authored-by: ruslandoga --- config/runtime.exs | 8 ++- lib/plausible_web/endpoint.ex | 62 ++++++++++++------- lib/plausible_web/live_socket.ex | 15 +++++ .../plugs/runtime_session_adapter.ex | 10 ++- 4 files changed, 68 insertions(+), 27 deletions(-) create mode 100644 lib/plausible_web/live_socket.ex diff --git a/config/runtime.exs b/config/runtime.exs index 9e26bdd6b..6b02434b8 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -232,6 +232,11 @@ if byte_size(websocket_url) > 0 and """ end +secure_cookie = + config_dir + |> get_var_from_path_or_env("SECURE_COOKIE", if(is_selfhost, do: "false", else: "true")) + |> String.to_existing_atom() + config :plausible, environment: env, mailer_email: mailer_email, @@ -254,7 +259,8 @@ config :plausible, PlausibleWeb.Endpoint, protocol_options: [max_request_line_length: 8192, max_header_value_length: 8192] ], secret_key_base: secret_key_base, - websocket_url: websocket_url + websocket_url: websocket_url, + secure_cookie: secure_cookie maybe_ipv6 = if System.get_env("ECTO_IPV6"), do: [:inet6], else: [] diff --git a/lib/plausible_web/endpoint.ex b/lib/plausible_web/endpoint.ex index 8009ccdc1..5c1611a54 100644 --- a/lib/plausible_web/endpoint.ex +++ b/lib/plausible_web/endpoint.ex @@ -3,9 +3,10 @@ defmodule PlausibleWeb.Endpoint do use Phoenix.Endpoint, otp_app: :plausible @session_options [ + # key to be patched + key: "", store: :cookie, - key: "_plausible_key", - signing_salt: "3IL0ob4k", + signing_salt: "I45i0SKHEku2f3tJh6y4v8gztrb/eG5KGCOe/o/AwFb7VHeuvDOn7AAq6KsdmOFM", # 5 years, this is super long but the SlidingSessionTimeout will log people out if they don't return for 2 weeks max_age: 60 * 60 * 24 * 365 * 5, extra: "SameSite=Lax" @@ -16,58 +17,62 @@ defmodule PlausibleWeb.Endpoint do # # You should set gzip to true if you are running phx.digest # when deploying your static files in production. - plug PlausibleWeb.Tracker - plug PlausibleWeb.Favicon + plug(PlausibleWeb.Tracker) + plug(PlausibleWeb.Favicon) - plug Plug.Static, + plug(Plug.Static, at: "/", from: :plausible, gzip: false, only: ~w(css images js favicon.ico robots.txt) + ) - plug Plug.Static, + plug(Plug.Static, at: "/kaffy", from: :kaffy, gzip: false, only: ~w(assets) + ) # Code reloading can be explicitly enabled under the # :code_reloader configuration of your endpoint. if code_reloading? do - socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket - plug Phoenix.LiveReloader - plug Phoenix.CodeReloader + socket("/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket) + plug(Phoenix.LiveReloader) + plug(Phoenix.CodeReloader) end - plug Plug.RequestId - plug PromEx.Plug, prom_ex_module: Plausible.PromEx - plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint] + plug(Plug.RequestId) + plug(PromEx.Plug, prom_ex_module: Plausible.PromEx) + plug(Plug.Telemetry, event_prefix: [:phoenix, :endpoint]) - plug Plug.Parsers, + plug(Plug.Parsers, parsers: [:urlencoded, :multipart, :json], pass: ["*/*"], json_decoder: Phoenix.json_library() + ) - plug Sentry.PlugContext + plug(Sentry.PlugContext) - plug Plug.MethodOverride - plug Plug.Head + plug(Plug.MethodOverride) + plug(Plug.Head) - plug PlausibleWeb.Plugs.RuntimeSessionAdapter, @session_options + plug(PlausibleWeb.Plugs.RuntimeSessionAdapter, @session_options) - socket "/live", Phoenix.LiveView.Socket, + socket("/live", PlausibleWeb.LiveSocket, websocket: [ check_origin: true, connect_info: [session: {__MODULE__, :patch_session_opts, []}] ] + ) - plug CORSPlug - plug PlausibleWeb.Router + plug(CORSPlug) + plug(PlausibleWeb.Router) + + def secure_cookie?, do: config!(:secure_cookie) def websocket_url() do - :plausible - |> Application.fetch_env!(__MODULE__) - |> Keyword.fetch!(:websocket_url) + config!(:websocket_url) end def patch_session_opts() do @@ -75,6 +80,15 @@ defmodule PlausibleWeb.Endpoint do # is used to inject the domain - this way we can authenticate # websocket requests within single root domain, in case websocket_url() # returns a ws{s}:// scheme (in which case SameSite=Lax is not applicable). - Keyword.put(@session_options, :domain, host()) + @session_options + |> Keyword.put(:domain, host()) + |> Keyword.put(:key, "_plausible_#{Application.fetch_env!(:plausible, :environment)}") + |> Keyword.put(:secure, secure_cookie?()) + end + + defp config!(key) do + :plausible + |> Application.fetch_env!(__MODULE__) + |> Keyword.fetch!(key) end end diff --git a/lib/plausible_web/live_socket.ex b/lib/plausible_web/live_socket.ex new file mode 100644 index 000000000..2f8fdaf48 --- /dev/null +++ b/lib/plausible_web/live_socket.ex @@ -0,0 +1,15 @@ +defmodule PlausibleWeb.LiveSocket do + use Phoenix.LiveView.Socket + require Logger + + def connect(params, %Phoenix.Socket{} = socket, connect_info) do + case Phoenix.LiveView.Socket.connect(params, socket, connect_info) do + {:ok, %Phoenix.Socket{private: %{connect_info: %{session: nil}}}} -> + Logger.error("Could not connect the live socket: no session found") + {:error, :socket_auth_error} + + other -> + other + end + end +end diff --git a/lib/plausible_web/plugs/runtime_session_adapter.ex b/lib/plausible_web/plugs/runtime_session_adapter.ex index ae5b1f221..f06c04736 100644 --- a/lib/plausible_web/plugs/runtime_session_adapter.ex +++ b/lib/plausible_web/plugs/runtime_session_adapter.ex @@ -21,10 +21,16 @@ defmodule PlausibleWeb.Plugs.RuntimeSessionAdapter do end defp patch_cookie_domain(%{cookie_opts: cookie_opts} = runtime_opts) do - Map.replace( + Map.put( runtime_opts, :cookie_opts, - Keyword.put_new(cookie_opts, :domain, PlausibleWeb.Endpoint.host()) + cookie_opts + |> Keyword.put_new(:domain, PlausibleWeb.Endpoint.host()) + |> Keyword.put( + :secure, + Application.fetch_env!(:plausible, PlausibleWeb.Endpoint)[:secure_cookie] + ) ) + |> Map.put(:key, "_plausible_#{Application.fetch_env!(:plausible, :environment)}") end end