Critical Fixes: - Remove /health/time endpoint exposing system time information * Prevents attackers from detecting time sync issues for TOTP attacks * Removed route and controller function, updated tests - Add email confirmation check to account data export * GDPR export now requires confirmed email address * Prevents unconfirmed accounts from accessing data export - Add path traversal validation for MIB archive uploads * Extract to temp directory, validate all paths, then copy if safe * Prevents malicious tar/zip files from writing outside target directory * Added validate_extracted_paths/1 helper function High Priority Fixes: - Add comprehensive input validation for mobile auth * Length limits: device_name (255), device_os (100), app_version (50), push_token (512) * Prevents database corruption and storage exhaustion - Add heartbeat rate limiting to agent channel * Limit database updates to once per 30 seconds (max ~2/min per agent) * Prevents malicious agents from exhausting database connections - Sanitize 500 error responses * Return generic error messages to clients * Log full details server-side with request_id for support * Prevents leaking stack traces and module names - Add message size limits to agent channel * 10MB maximum for all protobuf messages (result, heartbeat, error) * Prevents DoS attacks via oversized payloads Medium Priority Fixes: - Add GraphQL query depth limits (max_depth: 10) * Prevents DoS from deeply nested queries * Complements existing complexity limits Code Quality: - Refactor agent channel handlers to reduce nesting depth * Extract message processing into separate private functions * Fixes Credo warnings about excessive nesting * Improves code readability and maintainability Files changed: - lib/towerops_web/controllers/health_controller.ex - lib/towerops_web/controllers/api/account_data_controller.ex - lib/towerops_web/controllers/api/v1/mib_controller.ex - lib/towerops/mobile_sessions/mobile_session.ex - lib/towerops_web/channels/agent_channel.ex - lib/towerops_web/controllers/error_json.ex - lib/towerops_web/router.ex - CHANGELOG.txt - priv/static/changelog.txt - test/towerops_web/controllers/health_controller_test.exs - test/towerops_web/controllers/error_json_test.exs All 7,424 tests passing.
81 lines
2.1 KiB
Elixir
81 lines
2.1 KiB
Elixir
defmodule ToweropsWeb.HealthController do
|
|
@moduledoc false
|
|
|
|
use ToweropsWeb, :controller
|
|
|
|
alias Ecto.Adapters.SQL
|
|
alias Towerops.Repo
|
|
|
|
def index(conn, _params) do
|
|
require Logger
|
|
|
|
db_status = check_database()
|
|
redis_status = check_redis()
|
|
|
|
# Log health check results for debugging
|
|
Logger.debug("Health check: db=#{inspect(db_status)}, redis=#{inspect(redis_status)}")
|
|
|
|
all_healthy = db_status == :ok and redis_status in [:ok, :not_configured]
|
|
|
|
if all_healthy do
|
|
conn
|
|
|> put_resp_content_type("application/json")
|
|
|> send_resp(
|
|
200,
|
|
Jason.encode!(%{
|
|
status: "ok",
|
|
database: "connected",
|
|
redis: redis_status_string(redis_status),
|
|
version: :towerops |> Application.spec(:vsn) |> to_string()
|
|
})
|
|
)
|
|
else
|
|
conn
|
|
|> put_resp_content_type("application/json")
|
|
|> send_resp(
|
|
503,
|
|
Jason.encode!(%{
|
|
status: "error",
|
|
database: db_status_string(db_status),
|
|
redis: redis_status_string(redis_status)
|
|
})
|
|
)
|
|
end
|
|
end
|
|
|
|
defp check_database do
|
|
case SQL.query(Repo, "SELECT 1", []) do
|
|
{:ok, _} -> :ok
|
|
{:error, _} -> :error
|
|
end
|
|
end
|
|
|
|
defp check_redis do
|
|
require Logger
|
|
|
|
redis_config = Application.get_env(:towerops, :redis, [])
|
|
|
|
if Keyword.has_key?(redis_config, :host) do
|
|
# Redis is configured, check connectivity with short timeout for health check
|
|
case Towerops.RedisHealthCheck.check_health(redis_config, 2_000) do
|
|
:ok ->
|
|
:ok
|
|
|
|
{:error, reason} ->
|
|
Logger.error("Redis health check failed: #{inspect(reason)}")
|
|
:error
|
|
end
|
|
else
|
|
# Redis not configured (e.g., in development without Redis)
|
|
Logger.debug("Redis not configured")
|
|
:not_configured
|
|
end
|
|
end
|
|
|
|
defp db_status_string(:ok), do: "connected"
|
|
defp db_status_string(:error), do: "disconnected"
|
|
|
|
defp redis_status_string(:ok), do: "connected"
|
|
defp redis_status_string(:error), do: "disconnected"
|
|
defp redis_status_string(:not_configured), do: "not_configured"
|
|
end
|