mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 18:18:55 +00:00
When giving TURN credentials to clients and gateways, it's important that they remain consistent across hiccups in the portal connection so that relayed connections are not interrupted during a deploy, or if the user's internet is flaky, or the GCP load balancer decides to disconnect the client/gateway. Prior to this PR, that was not the case because we essentially tied TURN credentials, required for data plane packet flows, to the WebSocket connection, a control plane element. This happened because we generated random `expires_at` and `salt` elements on _each_ connection to the portal. Instead, what we do now is make these reproducible and tied to the auth token by hashing then base64-encoding it. The expiry is tied to the auth-token's expiry. Fixes #9856
65 lines
2.0 KiB
Elixir
65 lines
2.0 KiB
Elixir
defmodule API.Gateway.Socket do
|
|
use Phoenix.Socket
|
|
alias Domain.{Tokens, Gateways}
|
|
require Logger
|
|
require OpenTelemetry.Tracer
|
|
|
|
## Channels
|
|
|
|
channel "gateway", API.Gateway.Channel
|
|
|
|
## Authentication
|
|
|
|
@impl true
|
|
def connect(%{"token" => encoded_token} = attrs, socket, connect_info) do
|
|
:otel_propagator_text_map.extract(connect_info.trace_context_headers)
|
|
|
|
OpenTelemetry.Tracer.with_span "gateway.connect" do
|
|
context = API.Sockets.auth_context(connect_info, :gateway_group)
|
|
attrs = Map.take(attrs, ~w[external_id name public_key])
|
|
|
|
with {:ok, group, token} <- Gateways.authenticate(encoded_token, context),
|
|
{:ok, gateway} <- Gateways.upsert_gateway(group, token, attrs, context) do
|
|
OpenTelemetry.Tracer.set_attributes(%{
|
|
token_id: token.id,
|
|
gateway_id: gateway.id,
|
|
account_id: gateway.account_id,
|
|
version: gateway.last_seen_version
|
|
})
|
|
|
|
# For Relay credentials
|
|
turn_salt =
|
|
Domain.Crypto.hash(:sha256, encoded_token)
|
|
|> Base.url_encode64(padding: false)
|
|
|
|
socket =
|
|
socket
|
|
|> assign(:turn_salt, turn_salt)
|
|
|> assign(:token_id, token.id)
|
|
|> assign(:gateway_group, group)
|
|
|> assign(:gateway, gateway)
|
|
|> assign(:opentelemetry_span_ctx, OpenTelemetry.Tracer.current_span_ctx())
|
|
|> assign(:opentelemetry_ctx, OpenTelemetry.Ctx.get_current())
|
|
|
|
{:ok, socket}
|
|
else
|
|
{:error, :unauthorized} ->
|
|
OpenTelemetry.Tracer.set_status(:error, "invalid_token")
|
|
{:error, :invalid_token}
|
|
|
|
{:error, reason} ->
|
|
OpenTelemetry.Tracer.set_status(:error, inspect(reason))
|
|
Logger.debug("Error connecting gateway websocket: #{inspect(reason)}")
|
|
{:error, reason}
|
|
end
|
|
end
|
|
end
|
|
|
|
def connect(_params, _socket, _connect_info) do
|
|
{:error, :missing_token}
|
|
end
|
|
|
|
@impl true
|
|
def id(socket), do: Tokens.socket_id(socket.assigns.token_id)
|
|
end
|