feat: disable webhook/websocket by default

This commit is contained in:
guarzo
2025-06-21 16:31:47 -04:00
parent d261c6186b
commit 48ff2f4413
6 changed files with 62 additions and 12 deletions

View File

@@ -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,

View File

@@ -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

View File

@@ -15,6 +15,7 @@ defmodule WandererApp.Env do
def custom_route_base_url, do: get_key(:custom_route_base_url, "<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(

View File

@@ -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=<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
end

View File

@@ -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

View File

@@ -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