mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-12-16 12:46:04 +00:00
feat: disable webhook/websocket by default
This commit is contained in:
@@ -72,6 +72,11 @@ map_subscriptions_enabled =
|
|||||||
|> get_var_from_path_or_env("WANDERER_MAP_SUBSCRIPTIONS_ENABLED", "false")
|
|> get_var_from_path_or_env("WANDERER_MAP_SUBSCRIPTIONS_ENABLED", "false")
|
||||||
|> String.to_existing_atom()
|
|> 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 =
|
map_subscription_characters_limit =
|
||||||
config_dir
|
config_dir
|
||||||
|> get_int_from_path_or_env("WANDERER_MAP_SUBSCRIPTION_CHARACTERS_LIMIT", 10_000)
|
|> 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_service_enabled: wanderer_kills_service_enabled,
|
||||||
wanderer_kills_base_url: wanderer_kills_base_url,
|
wanderer_kills_base_url: wanderer_kills_base_url,
|
||||||
map_subscriptions_enabled: map_subscriptions_enabled,
|
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_expire_hours: map_connection_auto_expire_hours,
|
||||||
map_connection_auto_eol_hours: map_connection_auto_eol_hours,
|
map_connection_auto_eol_hours: map_connection_auto_eol_hours,
|
||||||
map_connection_eol_expire_timeout_mins: map_connection_eol_expire_timeout_mins,
|
map_connection_eol_expire_timeout_mins: map_connection_eol_expire_timeout_mins,
|
||||||
|
|||||||
@@ -56,13 +56,12 @@ defmodule WandererApp.Application do
|
|||||||
{WandererApp.Character.TrackerPoolSupervisor, []},
|
{WandererApp.Character.TrackerPoolSupervisor, []},
|
||||||
WandererApp.Character.TrackerManager,
|
WandererApp.Character.TrackerManager,
|
||||||
WandererApp.Map.Manager,
|
WandererApp.Map.Manager,
|
||||||
WandererApp.ExternalEvents.MapEventRelay,
|
|
||||||
WandererApp.ExternalEvents.WebhookDispatcher,
|
|
||||||
WandererAppWeb.Presence,
|
WandererAppWeb.Presence,
|
||||||
WandererAppWeb.Endpoint
|
WandererAppWeb.Endpoint
|
||||||
] ++
|
] ++
|
||||||
maybe_start_corp_wallet_tracker(WandererApp.Env.map_subscriptions_enabled?()) ++
|
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]
|
opts = [strategy: :one_for_one, name: WandererApp.Supervisor]
|
||||||
|
|
||||||
@@ -106,4 +105,15 @@ defmodule WandererApp.Application do
|
|||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|||||||
@@ -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 custom_route_base_url, do: get_key(:custom_route_base_url, "<CUSTOM_ROUTE_BASE_URL>")
|
||||||
def invites, do: get_key(:invites, false)
|
def invites, do: get_key(:invites, false)
|
||||||
def map_subscriptions_enabled?, do: get_key(:map_subscriptions_enabled, 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)
|
def public_api_disabled?, do: get_key(:public_api_disabled, false)
|
||||||
|
|
||||||
@decorate cacheable(
|
@decorate cacheable(
|
||||||
|
|||||||
@@ -1,19 +1,26 @@
|
|||||||
defmodule WandererAppWeb.UserSocket do
|
defmodule WandererAppWeb.UserSocket do
|
||||||
use Phoenix.Socket
|
use Phoenix.Socket
|
||||||
|
require Logger
|
||||||
|
|
||||||
# External events channel for webhooks/WebSocket delivery
|
# External events channel for webhooks/WebSocket delivery
|
||||||
channel "external_events:map:*", WandererAppWeb.MapEventsChannel
|
channel "external_events:map:*", WandererAppWeb.MapEventsChannel
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def connect(params, socket, connect_info) do
|
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
|
# Extract API key from connection params
|
||||||
# Client should connect with: /socket/websocket?api_key=<key>
|
# Client should connect with: /socket/websocket?api_key=<key>
|
||||||
require Logger
|
|
||||||
|
|
||||||
# Log connection attempt for security auditing
|
# Log connection attempt for security auditing
|
||||||
remote_ip = get_remote_ip(connect_info)
|
remote_ip = get_remote_ip(connect_info)
|
||||||
Logger.info("WebSocket connection attempt from #{remote_ip}")
|
Logger.info("WebSocket connection attempt from #{remote_ip}")
|
||||||
|
|
||||||
case params["api_key"] do
|
case params["api_key"] do
|
||||||
api_key when is_binary(api_key) and api_key != "" ->
|
api_key when is_binary(api_key) and api_key != "" ->
|
||||||
# Store the API key in socket assigns for channel authentication
|
# Store the API key in socket assigns for channel authentication
|
||||||
@@ -21,23 +28,24 @@ defmodule WandererAppWeb.UserSocket do
|
|||||||
socket = socket
|
socket = socket
|
||||||
|> assign(:api_key, api_key)
|
|> assign(:api_key, api_key)
|
||||||
|> assign(:remote_ip, remote_ip)
|
|> assign(:remote_ip, remote_ip)
|
||||||
|
|
||||||
Logger.info("WebSocket connection accepted from #{remote_ip}, pending channel authentication")
|
Logger.info("WebSocket connection accepted from #{remote_ip}, pending channel authentication")
|
||||||
{:ok, socket}
|
{:ok, socket}
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
# Require API key for external events
|
# Require API key for external events
|
||||||
Logger.warning("WebSocket connection rejected - missing API key from #{remote_ip}")
|
Logger.warning("WebSocket connection rejected - missing API key from #{remote_ip}")
|
||||||
:error
|
:error
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Extract remote IP from connection info
|
# Extract remote IP from connection info
|
||||||
defp get_remote_ip(connect_info) do
|
defp get_remote_ip(connect_info) do
|
||||||
case connect_info do
|
case connect_info do
|
||||||
%{peer_data: %{address: {a, b, c, d}}} ->
|
%{peer_data: %{address: {a, b, c, d}}} ->
|
||||||
"#{a}.#{b}.#{c}.#{d}"
|
"#{a}.#{b}.#{c}.#{d}"
|
||||||
|
|
||||||
%{x_headers: headers} ->
|
%{x_headers: headers} ->
|
||||||
# Check for X-Forwarded-For or X-Real-IP headers (for proxied connections)
|
# Check for X-Forwarded-For or X-Real-IP headers (for proxied connections)
|
||||||
Enum.find_value(headers, "unknown", fn
|
Enum.find_value(headers, "unknown", fn
|
||||||
@@ -45,7 +53,7 @@ defmodule WandererAppWeb.UserSocket do
|
|||||||
{"x-real-ip", ip} -> ip
|
{"x-real-ip", ip} -> ip
|
||||||
_ -> nil
|
_ -> nil
|
||||||
end)
|
end)
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
"unknown"
|
"unknown"
|
||||||
end
|
end
|
||||||
@@ -53,4 +61,4 @@ defmodule WandererAppWeb.UserSocket do
|
|||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def id(_socket), do: nil
|
def id(_socket), do: nil
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -180,6 +180,10 @@ defmodule WandererAppWeb.Router do
|
|||||||
plug WandererAppWeb.Plugs.CheckCharacterApiDisabled
|
plug WandererAppWeb.Plugs.CheckCharacterApiDisabled
|
||||||
end
|
end
|
||||||
|
|
||||||
|
pipeline :api_websocket_events do
|
||||||
|
plug WandererAppWeb.Plugs.CheckWebsocketDisabled
|
||||||
|
end
|
||||||
|
|
||||||
pipeline :api_acl do
|
pipeline :api_acl do
|
||||||
plug WandererAppWeb.Plugs.CheckAclApiKey
|
plug WandererAppWeb.Plugs.CheckAclApiKey
|
||||||
end
|
end
|
||||||
@@ -240,6 +244,12 @@ defmodule WandererAppWeb.Router do
|
|||||||
resources "/signatures", MapSystemSignatureAPIController, except: [:new, :edit]
|
resources "/signatures", MapSystemSignatureAPIController, except: [:new, :edit]
|
||||||
get "/user-characters", MapAPIController, :show_user_characters
|
get "/user-characters", MapAPIController, :show_user_characters
|
||||||
get "/tracked-characters", MapAPIController, :show_tracked_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
|
get "/events", MapEventsAPIController, :list_events
|
||||||
|
|
||||||
# Webhook management endpoints
|
# Webhook management endpoints
|
||||||
|
|||||||
Reference in New Issue
Block a user