From 48ff2f4413d20449ac1b66cb4cfb51a764c2e580 Mon Sep 17 00:00:00 2001 From: guarzo Date: Sat, 21 Jun 2025 16:31:47 -0400 Subject: [PATCH] feat: disable webhook/websocket by default --- config/runtime.exs | 6 +++++ lib/wanderer_app/application.ex | 16 +++++++++--- lib/wanderer_app/env.ex | 1 + lib/wanderer_app_web/channels/user_socket.ex | 26 ++++++++++++------- .../plugs/check_websocket_disabled.ex | 15 +++++++++++ lib/wanderer_app_web/router.ex | 10 +++++++ 6 files changed, 62 insertions(+), 12 deletions(-) create mode 100644 lib/wanderer_app_web/controllers/plugs/check_websocket_disabled.ex diff --git a/config/runtime.exs b/config/runtime.exs index d7eb76db..6cd35655 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -72,6 +72,11 @@ map_subscriptions_enabled = |> get_var_from_path_or_env("WANDERER_MAP_SUBSCRIPTIONS_ENABLED", "false") |> String.to_existing_atom() +websocket_events_enabled = + config_dir + |> get_var_from_path_or_env("WANDERER_WEBSOCKET_EVENTS_ENABLED", "false") + |> String.to_existing_atom() + map_subscription_characters_limit = config_dir |> get_int_from_path_or_env("WANDERER_MAP_SUBSCRIPTION_CHARACTERS_LIMIT", 10_000) @@ -143,6 +148,7 @@ config :wanderer_app, wanderer_kills_service_enabled: wanderer_kills_service_enabled, wanderer_kills_base_url: wanderer_kills_base_url, map_subscriptions_enabled: map_subscriptions_enabled, + websocket_events_enabled: websocket_events_enabled, map_connection_auto_expire_hours: map_connection_auto_expire_hours, map_connection_auto_eol_hours: map_connection_auto_eol_hours, map_connection_eol_expire_timeout_mins: map_connection_eol_expire_timeout_mins, diff --git a/lib/wanderer_app/application.ex b/lib/wanderer_app/application.ex index 25223926..3bed8488 100644 --- a/lib/wanderer_app/application.ex +++ b/lib/wanderer_app/application.ex @@ -56,13 +56,12 @@ defmodule WandererApp.Application do {WandererApp.Character.TrackerPoolSupervisor, []}, WandererApp.Character.TrackerManager, WandererApp.Map.Manager, - WandererApp.ExternalEvents.MapEventRelay, - WandererApp.ExternalEvents.WebhookDispatcher, WandererAppWeb.Presence, WandererAppWeb.Endpoint ] ++ maybe_start_corp_wallet_tracker(WandererApp.Env.map_subscriptions_enabled?()) ++ - maybe_start_kills_services() + maybe_start_kills_services() ++ + maybe_start_websocket_services(WandererApp.Env.websocket_events_enabled?()) opts = [strategy: :one_for_one, name: WandererApp.Supervisor] @@ -106,4 +105,15 @@ defmodule WandererApp.Application do [] end end + + defp maybe_start_websocket_services(true) do + Logger.info("Starting WebSocket/Webhook services...") + + [ + WandererApp.ExternalEvents.MapEventRelay, + WandererApp.ExternalEvents.WebhookDispatcher + ] + end + + defp maybe_start_websocket_services(_), do: [] end diff --git a/lib/wanderer_app/env.ex b/lib/wanderer_app/env.ex index 3fbaae9d..a031486a 100644 --- a/lib/wanderer_app/env.ex +++ b/lib/wanderer_app/env.ex @@ -15,6 +15,7 @@ defmodule WandererApp.Env do def custom_route_base_url, do: get_key(:custom_route_base_url, "") def invites, do: get_key(:invites, false) def map_subscriptions_enabled?, do: get_key(:map_subscriptions_enabled, false) + def websocket_events_enabled?, do: get_key(:websocket_events_enabled, false) def public_api_disabled?, do: get_key(:public_api_disabled, false) @decorate cacheable( diff --git a/lib/wanderer_app_web/channels/user_socket.ex b/lib/wanderer_app_web/channels/user_socket.ex index 428464d4..b8cb8f0e 100644 --- a/lib/wanderer_app_web/channels/user_socket.ex +++ b/lib/wanderer_app_web/channels/user_socket.ex @@ -1,19 +1,26 @@ defmodule WandererAppWeb.UserSocket do use Phoenix.Socket + require Logger # External events channel for webhooks/WebSocket delivery channel "external_events:map:*", WandererAppWeb.MapEventsChannel @impl true def connect(params, socket, connect_info) do + # Check if websocket events are enabled + unless WandererApp.Env.websocket_events_enabled?() do + remote_ip = get_remote_ip(connect_info) + Logger.info("WebSocket connection rejected - websocket events disabled from #{remote_ip}") + :error + else + # Extract API key from connection params # Client should connect with: /socket/websocket?api_key= - require Logger - + # Log connection attempt for security auditing remote_ip = get_remote_ip(connect_info) Logger.info("WebSocket connection attempt from #{remote_ip}") - + case params["api_key"] do api_key when is_binary(api_key) and api_key != "" -> # Store the API key in socket assigns for channel authentication @@ -21,23 +28,24 @@ defmodule WandererAppWeb.UserSocket do socket = socket |> assign(:api_key, api_key) |> assign(:remote_ip, remote_ip) - + Logger.info("WebSocket connection accepted from #{remote_ip}, pending channel authentication") {:ok, socket} - + _ -> # Require API key for external events Logger.warning("WebSocket connection rejected - missing API key from #{remote_ip}") :error end + end end - + # Extract remote IP from connection info defp get_remote_ip(connect_info) do case connect_info do %{peer_data: %{address: {a, b, c, d}}} -> "#{a}.#{b}.#{c}.#{d}" - + %{x_headers: headers} -> # Check for X-Forwarded-For or X-Real-IP headers (for proxied connections) Enum.find_value(headers, "unknown", fn @@ -45,7 +53,7 @@ defmodule WandererAppWeb.UserSocket do {"x-real-ip", ip} -> ip _ -> nil end) - + _ -> "unknown" end @@ -53,4 +61,4 @@ defmodule WandererAppWeb.UserSocket do @impl true def id(_socket), do: nil -end \ No newline at end of file +end diff --git a/lib/wanderer_app_web/controllers/plugs/check_websocket_disabled.ex b/lib/wanderer_app_web/controllers/plugs/check_websocket_disabled.ex new file mode 100644 index 00000000..af8a2123 --- /dev/null +++ b/lib/wanderer_app_web/controllers/plugs/check_websocket_disabled.ex @@ -0,0 +1,15 @@ +defmodule WandererAppWeb.Plugs.CheckWebsocketDisabled do + import Plug.Conn + + def init(opts), do: opts + + def call(conn, _opts) do + if not WandererApp.Env.websocket_events_enabled?() do + conn + |> send_resp(403, "WebSocket events are disabled") + |> halt() + else + conn + end + end +end \ No newline at end of file diff --git a/lib/wanderer_app_web/router.ex b/lib/wanderer_app_web/router.ex index 1c3ca208..6a3c6f95 100644 --- a/lib/wanderer_app_web/router.ex +++ b/lib/wanderer_app_web/router.ex @@ -180,6 +180,10 @@ defmodule WandererAppWeb.Router do plug WandererAppWeb.Plugs.CheckCharacterApiDisabled end + pipeline :api_websocket_events do + plug WandererAppWeb.Plugs.CheckWebsocketDisabled + end + pipeline :api_acl do plug WandererAppWeb.Plugs.CheckAclApiKey end @@ -240,6 +244,12 @@ defmodule WandererAppWeb.Router do resources "/signatures", MapSystemSignatureAPIController, except: [:new, :edit] get "/user-characters", MapAPIController, :show_user_characters get "/tracked-characters", MapAPIController, :show_tracked_characters + end + + # WebSocket events and webhook management endpoints (disabled by default) + scope "/api/maps/:map_identifier", WandererAppWeb do + pipe_through [:api, :api_map, :api_websocket_events] + get "/events", MapEventsAPIController, :list_events # Webhook management endpoints