mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 10:18:54 +00:00
feat(gateway): extend flow logs with more client properties (#10717)
In order to make the flow logs emitted by the Gateway more useful and self-contained, we extend the `authorize_flow` message sent to the Gateway with some more context around the Client and Actor of that flow. In particular, we now also send the following to the Gateway: - `client_version` - `device_os_version` - `device_os_name` - `device_serial` - `device_uuid` - `device_identifier_for_vendor` - `device_firebase_installation_id` - `identity_id` - `identity_name` - `actor_id` - `actor_email` We only extend the `authorize_flow` message with these additional properties. The legacy messages for 1.3.x Clients remain as is. For those Clients, the above properties will be empty in the flow logs. Resolves: #10690 --------- Signed-off-by: Thomas Eizinger <thomas@eizinger.io> Co-authored-by: Jamil <jamilbk@users.noreply.github.com>
This commit is contained in:
@@ -377,7 +377,8 @@ defmodule API.Client.Channel do
|
||||
flow_id: flow.id,
|
||||
authorization_expires_at: expires_at,
|
||||
ice_credentials: ice_credentials,
|
||||
preshared_key: preshared_key
|
||||
preshared_key: preshared_key,
|
||||
subject: socket.assigns.subject
|
||||
}}
|
||||
)
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ defmodule API.Gateway.Channel do
|
||||
Changes.Change,
|
||||
Flows,
|
||||
Gateways,
|
||||
Auth,
|
||||
PubSub,
|
||||
Relays,
|
||||
Resources
|
||||
@@ -226,7 +227,8 @@ defmodule API.Gateway.Channel do
|
||||
flow_id: flow_id,
|
||||
authorization_expires_at: authorization_expires_at,
|
||||
ice_credentials: ice_credentials,
|
||||
preshared_key: preshared_key
|
||||
preshared_key: preshared_key,
|
||||
subject: %Auth.Subject{} = subject
|
||||
} = payload
|
||||
|
||||
ref =
|
||||
@@ -244,7 +246,8 @@ defmodule API.Gateway.Channel do
|
||||
gateway_ice_credentials: ice_credentials.gateway,
|
||||
client: Views.Client.render(client, preshared_key),
|
||||
client_ice_credentials: ice_credentials.client,
|
||||
expires_at: DateTime.to_unix(authorization_expires_at, :second)
|
||||
expires_at: DateTime.to_unix(authorization_expires_at, :second),
|
||||
subject: Views.Subject.render(subject)
|
||||
})
|
||||
|
||||
cache =
|
||||
|
||||
@@ -2,12 +2,32 @@ defmodule API.Gateway.Views.Client do
|
||||
alias Domain.Clients
|
||||
|
||||
def render(%Clients.Client{} = client, preshared_key) do
|
||||
# The OS name can have spaces, hence split the user-agent step by step.
|
||||
[os_name, rest] = String.split(client.last_seen_user_agent, "/", parts: 2)
|
||||
[os_version, rest] = String.split(rest, " ", parts: 2)
|
||||
[_, rest] = String.split(rest, "/", parts: 2)
|
||||
|
||||
# TODO: For easier testing, we re-parse the client version here.
|
||||
# Long term, we should not be parsing the user-agent at all in here.
|
||||
# Instead we should directly store the parsed information in the DB.
|
||||
[client_version | _] = String.split(rest, " ", parts: 2)
|
||||
|
||||
# Note: We purposely omit the client_type as that will say `connlib` for older clients
|
||||
# (we've only recently changed this to `apple-client` etc).
|
||||
|
||||
%{
|
||||
id: client.id,
|
||||
public_key: client.public_key,
|
||||
preshared_key: preshared_key,
|
||||
ipv4: client.ipv4,
|
||||
ipv6: client.ipv6
|
||||
ipv6: client.ipv6,
|
||||
version: client_version,
|
||||
device_serial: client.device_serial,
|
||||
device_os_name: os_name,
|
||||
device_os_version: os_version,
|
||||
device_uuid: client.device_uuid,
|
||||
firebase_installation_id: client.firebase_installation_id,
|
||||
identifier_for_vendor: client.identifier_for_vendor
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
12
elixir/apps/api/lib/api/gateway/views/subject.ex
Normal file
12
elixir/apps/api/lib/api/gateway/views/subject.ex
Normal file
@@ -0,0 +1,12 @@
|
||||
defmodule API.Gateway.Views.Subject do
|
||||
alias Domain.Auth
|
||||
|
||||
def render(%Auth.Subject{} = subject) do
|
||||
%{
|
||||
identity_id: get_in(subject, [Access.key(:identity), Access.key(:id)]),
|
||||
identity_name: subject.actor.name,
|
||||
actor_id: subject.actor.id,
|
||||
actor_email: get_in(subject, [Access.key(:identity), Access.key(:email)])
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -2823,7 +2823,7 @@ defmodule API.Client.ChannelTest do
|
||||
account: account,
|
||||
group: internet_gateway_group,
|
||||
context: %{
|
||||
user_agent: "iOS/12.5 (iPhone) connlib/1.2.0"
|
||||
user_agent: "iOS/12.5 connlib/1.2.0"
|
||||
}
|
||||
)
|
||||
|> Repo.preload(:group)
|
||||
@@ -2841,7 +2841,7 @@ defmodule API.Client.ChannelTest do
|
||||
account: account,
|
||||
group: internet_gateway_group,
|
||||
context: %{
|
||||
user_agent: "iOS/12.5 (iPhone) connlib/1.3.0"
|
||||
user_agent: "iOS/12.5 connlib/1.3.0"
|
||||
}
|
||||
)
|
||||
|> Repo.preload(:group)
|
||||
|
||||
@@ -10,7 +10,7 @@ defmodule API.Client.SocketTest do
|
||||
]
|
||||
|
||||
@connect_info %{
|
||||
user_agent: "iOS/12.7 (iPhone) connlib/1.3.0",
|
||||
user_agent: "iOS/12.7 connlib/1.3.0",
|
||||
peer_data: %{address: {189, 172, 73, 001}},
|
||||
x_headers:
|
||||
[
|
||||
|
||||
@@ -117,7 +117,8 @@ defmodule API.Gateway.ChannelTest do
|
||||
client: client,
|
||||
resource: resource,
|
||||
socket: socket,
|
||||
gateway: gateway
|
||||
gateway: gateway,
|
||||
subject: subject
|
||||
} do
|
||||
expired_flow =
|
||||
Fixtures.Flows.create_flow(
|
||||
@@ -146,7 +147,8 @@ defmodule API.Gateway.ChannelTest do
|
||||
flow_id: expired_flow.id,
|
||||
authorization_expires_at: expired_expiration,
|
||||
ice_credentials: ice_credentials,
|
||||
preshared_key: preshared_key
|
||||
preshared_key: preshared_key,
|
||||
subject: subject
|
||||
}}
|
||||
)
|
||||
|
||||
@@ -177,7 +179,8 @@ defmodule API.Gateway.ChannelTest do
|
||||
client: client,
|
||||
resource: resource,
|
||||
socket: socket,
|
||||
gateway: gateway
|
||||
gateway: gateway,
|
||||
subject: subject
|
||||
} do
|
||||
expired_flow =
|
||||
Fixtures.Flows.create_flow(
|
||||
@@ -216,7 +219,8 @@ defmodule API.Gateway.ChannelTest do
|
||||
flow_id: expired_flow.id,
|
||||
authorization_expires_at: expired_expiration,
|
||||
ice_credentials: ice_credentials,
|
||||
preshared_key: preshared_key
|
||||
preshared_key: preshared_key,
|
||||
subject: subject
|
||||
}}
|
||||
)
|
||||
|
||||
@@ -231,7 +235,8 @@ defmodule API.Gateway.ChannelTest do
|
||||
flow_id: unexpired_flow.id,
|
||||
authorization_expires_at: unexpired_expiration,
|
||||
ice_credentials: ice_credentials,
|
||||
preshared_key: preshared_key
|
||||
preshared_key: preshared_key,
|
||||
subject: subject
|
||||
}}
|
||||
)
|
||||
|
||||
@@ -1488,7 +1493,8 @@ defmodule API.Gateway.ChannelTest do
|
||||
account: account,
|
||||
gateway: gateway,
|
||||
resource: resource,
|
||||
socket: socket
|
||||
socket: socket,
|
||||
subject: subject
|
||||
} do
|
||||
flow =
|
||||
Fixtures.Flows.create_flow(
|
||||
@@ -1516,7 +1522,8 @@ defmodule API.Gateway.ChannelTest do
|
||||
flow_id: flow.id,
|
||||
authorization_expires_at: expires_at,
|
||||
ice_credentials: ice_credentials,
|
||||
preshared_key: preshared_key
|
||||
preshared_key: preshared_key,
|
||||
subject: subject
|
||||
}}
|
||||
)
|
||||
|
||||
@@ -1542,7 +1549,22 @@ defmodule API.Gateway.ChannelTest do
|
||||
ipv4: client.ipv4,
|
||||
ipv6: client.ipv6,
|
||||
preshared_key: preshared_key,
|
||||
public_key: client.public_key
|
||||
public_key: client.public_key,
|
||||
version: client.last_seen_version,
|
||||
device_serial: client.device_serial,
|
||||
device_uuid: client.device_uuid,
|
||||
identifier_for_vendor: client.identifier_for_vendor,
|
||||
firebase_installation_id: client.firebase_installation_id,
|
||||
# Hardcode these to avoid having to reparse the user agent.
|
||||
device_os_name: "iOS",
|
||||
device_os_version: "12.5"
|
||||
}
|
||||
|
||||
assert payload.subject == %{
|
||||
identity_id: subject.identity.id,
|
||||
identity_name: subject.actor.name,
|
||||
actor_id: subject.actor.id,
|
||||
actor_email: subject.identity.email
|
||||
}
|
||||
|
||||
assert payload.client_ice_credentials == ice_credentials.client
|
||||
@@ -1588,7 +1610,8 @@ defmodule API.Gateway.ChannelTest do
|
||||
flow_id: flow.id,
|
||||
authorization_expires_at: expires_at,
|
||||
ice_credentials: ice_credentials,
|
||||
preshared_key: preshared_key
|
||||
preshared_key: preshared_key,
|
||||
subject: subject
|
||||
}}
|
||||
)
|
||||
|
||||
@@ -1629,7 +1652,8 @@ defmodule API.Gateway.ChannelTest do
|
||||
account: account,
|
||||
resource: resource,
|
||||
gateway: gateway,
|
||||
socket: socket
|
||||
socket: socket,
|
||||
subject: subject
|
||||
} do
|
||||
flow =
|
||||
Fixtures.Flows.create_flow(
|
||||
@@ -1663,7 +1687,8 @@ defmodule API.Gateway.ChannelTest do
|
||||
flow_id: flow.id,
|
||||
authorization_expires_at: expires_at,
|
||||
ice_credentials: ice_credentials,
|
||||
preshared_key: preshared_key
|
||||
preshared_key: preshared_key,
|
||||
subject: subject
|
||||
}}
|
||||
)
|
||||
|
||||
|
||||
92
elixir/apps/api/test/api/gateway/views/client_test.exs
Normal file
92
elixir/apps/api/test/api/gateway/views/client_test.exs
Normal file
@@ -0,0 +1,92 @@
|
||||
# The user-agents in this file have been taken from the production DB.
|
||||
|
||||
defmodule API.Gateway.Views.ClientTest do
|
||||
use ExUnit.Case, async: true
|
||||
alias Domain.Clients
|
||||
|
||||
describe "render/2" do
|
||||
test "parses_linux_headless_user_agent" do
|
||||
client = %Clients.Client{
|
||||
last_seen_user_agent: "Ubuntu/22.4.0 headless-client/1.5.4 (x86_64; 6.8.0-1036-azure)"
|
||||
}
|
||||
|
||||
view = API.Gateway.Views.Client.render(client, "")
|
||||
|
||||
assert view.device_os_name == "Ubuntu"
|
||||
assert view.device_os_version == "22.4.0"
|
||||
assert view.version == "1.5.4"
|
||||
end
|
||||
|
||||
test "parses_macos_user_agent" do
|
||||
client = %Clients.Client{
|
||||
last_seen_user_agent: "Mac OS/15.4.1 apple-client/1.5.8 (arm64; 24.4.0)"
|
||||
}
|
||||
|
||||
view = API.Gateway.Views.Client.render(client, "")
|
||||
|
||||
assert view.device_os_name == "Mac OS"
|
||||
assert view.device_os_version == "15.4.1"
|
||||
assert view.version == "1.5.8"
|
||||
end
|
||||
|
||||
test "parses_android_user_agent" do
|
||||
client = %Clients.Client{
|
||||
last_seen_user_agent: "Android/12 android-client/1.5.2 (4.14.180-perf+)"
|
||||
}
|
||||
|
||||
view = API.Gateway.Views.Client.render(client, "")
|
||||
|
||||
assert view.device_os_name == "Android"
|
||||
assert view.device_os_version == "12"
|
||||
assert view.version == "1.5.2"
|
||||
end
|
||||
|
||||
test "parses_windows_gui_user_agent" do
|
||||
client = %Clients.Client{
|
||||
last_seen_user_agent: "Windows/10.0.26200 gui-client/1.5.8 (x86_64)"
|
||||
}
|
||||
|
||||
view = API.Gateway.Views.Client.render(client, "")
|
||||
|
||||
assert view.device_os_name == "Windows"
|
||||
assert view.device_os_version == "10.0.26200"
|
||||
assert view.version == "1.5.8"
|
||||
end
|
||||
|
||||
test "parses_ios_user_agent" do
|
||||
client = %Clients.Client{
|
||||
last_seen_user_agent: "iOS/26.0.1 apple-client/1.5.8 (25.0.0)"
|
||||
}
|
||||
|
||||
view = API.Gateway.Views.Client.render(client, "")
|
||||
|
||||
assert view.device_os_name == "iOS"
|
||||
assert view.device_os_version == "26.0.1"
|
||||
assert view.version == "1.5.8"
|
||||
end
|
||||
|
||||
test "parses_pop_os_user_agent" do
|
||||
client = %Clients.Client{
|
||||
last_seen_user_agent: "Pop!_OS/24.4.0 gui-client/1.5.8 (x86_64; 6.16.3-76061603-generic)"
|
||||
}
|
||||
|
||||
view = API.Gateway.Views.Client.render(client, "")
|
||||
|
||||
assert view.device_os_name == "Pop!_OS"
|
||||
assert view.device_os_version == "24.4.0"
|
||||
assert view.version == "1.5.8"
|
||||
end
|
||||
|
||||
test "parses_user_agent_without_additional_data" do
|
||||
client = %Clients.Client{
|
||||
last_seen_user_agent: "iOS/26.0.1 apple-client/1.5.8"
|
||||
}
|
||||
|
||||
view = API.Gateway.Views.Client.render(client, "")
|
||||
|
||||
assert view.device_os_name == "iOS"
|
||||
assert view.device_os_version == "26.0.1"
|
||||
assert view.version == "1.5.8"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -36,7 +36,7 @@ defmodule Domain.Fixtures.Auth do
|
||||
|
||||
def user_password, do: "Hello w0rld!"
|
||||
def remote_ip, do: {100, 64, 100, 58}
|
||||
def user_agent, do: "iOS/12.5 (iPhone) connlib/1.3.0"
|
||||
def user_agent, do: "iOS/12.5 connlib/1.3.0"
|
||||
def email(domain \\ "example.com"), do: "user-#{unique_integer()}@#{domain}"
|
||||
|
||||
def random_provider_identifier(%Domain.Auth.Provider{adapter: :email, name: name}) do
|
||||
|
||||
@@ -7,7 +7,7 @@ defmodule Domain.Fixtures.Clients do
|
||||
external_id: Ecto.UUID.generate(),
|
||||
name: "client-#{unique_integer()}",
|
||||
public_key: unique_public_key(),
|
||||
last_seen_user_agent: "iOS/12.7 (iPhone) connlib/1.3.0",
|
||||
last_seen_user_agent: "iOS/18.6.2 apple-client/1.5.8 (24.6.0)",
|
||||
last_seen_remote_ip: Enum.random([unique_ipv4(), unique_ipv6()]),
|
||||
last_seen_remote_ip_location_region: "US",
|
||||
last_seen_remote_ip_location_city: "San Francisco",
|
||||
|
||||
@@ -224,6 +224,7 @@ redundant_clone = "warn"
|
||||
unwrap_in_result = "warn"
|
||||
unwrap_used = "warn"
|
||||
too_many_arguments = "allow" # Don't care.
|
||||
large_enum_variant = "allow" # Don't care.
|
||||
|
||||
[workspace.lints.rustdoc]
|
||||
private-intra-doc-links = "allow" # We don't publish any of our docs but want to catch dead links.
|
||||
|
||||
@@ -7,8 +7,8 @@ pub(crate) use crate::gateway::client_on_gateway::ClientOnGateway;
|
||||
|
||||
use crate::gateway::client_on_gateway::TranslateOutboundResult;
|
||||
use crate::gateway::flow_tracker::FlowTracker;
|
||||
use crate::messages::gateway::ResourceDescription;
|
||||
use crate::messages::{Answer, IceCredentials, ResolveRequest, SecretKey};
|
||||
use crate::messages::gateway::{Client, ResourceDescription, Subject};
|
||||
use crate::messages::{Answer, IceCredentials, ResolveRequest};
|
||||
use crate::peer_store::PeerStore;
|
||||
use crate::{GatewayEvent, IpConfig, p2p_control};
|
||||
use anyhow::{Context, Result};
|
||||
@@ -169,7 +169,6 @@ impl GatewayState {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
flow_tracker::inbound_wg::record_client(cid);
|
||||
flow_tracker::inbound_wg::record_decrypted_packet(&packet);
|
||||
|
||||
let peer = self
|
||||
@@ -177,6 +176,8 @@ impl GatewayState {
|
||||
.get_mut(&cid)
|
||||
.with_context(|| format!("No peer for connection {cid}"))?;
|
||||
|
||||
flow_tracker::inbound_wg::record_client(cid, peer.client_flow_properties());
|
||||
|
||||
if let Some(fz_p2p_control) = packet.as_fz_p2p_control() {
|
||||
let immediate_response = match fz_p2p_control.event_type() {
|
||||
p2p_control::ASSIGNED_IPS_EVENT => {
|
||||
@@ -301,23 +302,21 @@ impl GatewayState {
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all, fields(%cid))]
|
||||
#[tracing::instrument(level = "debug", skip_all, fields(cid = %client.id))]
|
||||
pub fn authorize_flow(
|
||||
&mut self,
|
||||
cid: ClientId,
|
||||
client_key: PublicKey,
|
||||
preshared_key: SecretKey,
|
||||
client: Client,
|
||||
subject: Subject,
|
||||
client_ice: IceCredentials,
|
||||
gateway_ice: IceCredentials,
|
||||
client_tun: IpConfig,
|
||||
expires_at: Option<DateTime<Utc>>,
|
||||
resource: ResourceDescription,
|
||||
now: Instant,
|
||||
) -> Result<(), NoTurnServers> {
|
||||
self.node.upsert_connection(
|
||||
cid,
|
||||
client_key,
|
||||
x25519::StaticSecret::from(preshared_key.expose_secret().0),
|
||||
client.id,
|
||||
client.public_key.into(),
|
||||
x25519::StaticSecret::from(client.preshared_key.expose_secret().0),
|
||||
Credentials {
|
||||
username: gateway_ice.username,
|
||||
password: gateway_ice.password,
|
||||
@@ -329,7 +328,29 @@ impl GatewayState {
|
||||
now,
|
||||
)?;
|
||||
|
||||
let result = self.allow_access(cid, client_tun, expires_at, resource, None);
|
||||
let result = self.allow_access(
|
||||
client.id,
|
||||
IpConfig {
|
||||
v4: client.ipv4,
|
||||
v6: client.ipv6,
|
||||
},
|
||||
flow_tracker::ClientProperties {
|
||||
version: client.version,
|
||||
device_os_name: client.device_os_name,
|
||||
device_os_version: client.device_os_version,
|
||||
device_serial: client.device_serial,
|
||||
device_uuid: client.device_uuid,
|
||||
identifier_for_vendor: client.identifier_for_vendor,
|
||||
firebase_installation_id: client.firebase_installation_id,
|
||||
identity_id: subject.identity_id,
|
||||
identity_name: subject.identity_name,
|
||||
actor_id: subject.actor_id,
|
||||
actor_email: subject.actor_email,
|
||||
},
|
||||
expires_at,
|
||||
resource,
|
||||
None,
|
||||
);
|
||||
debug_assert!(
|
||||
result.is_ok(),
|
||||
"`allow_access` should never fail without a `DnsResourceEntry`"
|
||||
@@ -342,6 +363,7 @@ impl GatewayState {
|
||||
&mut self,
|
||||
client: ClientId,
|
||||
client_tun: IpConfig,
|
||||
client_props: flow_tracker::ClientProperties,
|
||||
expires_at: Option<DateTime<Utc>>,
|
||||
resource: ResourceDescription,
|
||||
dns_resource_nat: Option<DnsResourceNatEntry>,
|
||||
@@ -351,7 +373,7 @@ impl GatewayState {
|
||||
let peer = self
|
||||
.peers
|
||||
.entry(client)
|
||||
.or_insert_with(|| ClientOnGateway::new(client, client_tun, gateway_tun));
|
||||
.or_insert_with(|| ClientOnGateway::new(client, client_tun, gateway_tun, client_props));
|
||||
|
||||
peer.add_resource(resource.clone(), expires_at);
|
||||
|
||||
@@ -465,7 +487,21 @@ impl GatewayState {
|
||||
tracing::trace!(
|
||||
target: "flow_logs::tcp",
|
||||
|
||||
client_id = %flow.client,
|
||||
client_id = %flow.client_id,
|
||||
client_version = flow.client_version.map(tracing::field::display),
|
||||
|
||||
device_os_name = flow.device_os_name.map(tracing::field::display),
|
||||
device_os_version = flow.device_os_version.map(tracing::field::display),
|
||||
device_serial = flow.device_serial.map(tracing::field::display),
|
||||
device_uuid = flow.device_uuid.map(tracing::field::display),
|
||||
device_identifier_for_vendor = flow.device_identifier_for_vendor.map(tracing::field::display),
|
||||
device_firebase_installation_id = flow.device_firebase_installation_id.map(tracing::field::display),
|
||||
|
||||
identity_id = flow.identity_id.map(tracing::field::display),
|
||||
identity_name = flow.identity_name.map(tracing::field::display),
|
||||
actor_id = flow.actor_id.map(tracing::field::display),
|
||||
actor_email = flow.actor_email.map(tracing::field::display),
|
||||
|
||||
resource_id = %flow.resource_id,
|
||||
resource_name = %flow.resource_name,
|
||||
resource_address = %flow.resource_address,
|
||||
@@ -495,7 +531,21 @@ impl GatewayState {
|
||||
tracing::trace!(
|
||||
target: "flow_logs::udp",
|
||||
|
||||
client_id = %flow.client,
|
||||
client_id = %flow.client_id,
|
||||
client_version = flow.client_version.map(tracing::field::display),
|
||||
|
||||
device_os_name = flow.device_os_name.map(tracing::field::display),
|
||||
device_os_version = flow.device_os_version.map(tracing::field::display),
|
||||
device_serial = flow.device_serial.map(tracing::field::display),
|
||||
device_uuid = flow.device_uuid.map(tracing::field::display),
|
||||
device_identifier_for_vendor = flow.device_identifier_for_vendor.map(tracing::field::display),
|
||||
device_firebase_installation_id = flow.device_firebase_installation_id.map(tracing::field::display),
|
||||
|
||||
identity_id = flow.identity_id.map(tracing::field::display),
|
||||
identity_name = flow.identity_name.map(tracing::field::display),
|
||||
actor_id = flow.actor_id.map(tracing::field::display),
|
||||
actor_email = flow.actor_email.map(tracing::field::display),
|
||||
|
||||
resource_id = %flow.resource_id,
|
||||
resource_name = %flow.resource_name,
|
||||
resource_address = %flow.resource_address,
|
||||
|
||||
@@ -28,6 +28,8 @@ pub struct ClientOnGateway {
|
||||
client_tun: IpConfig,
|
||||
gateway_tun: IpConfig,
|
||||
|
||||
flow_properties: flow_tracker::ClientProperties,
|
||||
|
||||
resources: BTreeMap<ResourceId, ResourceOnGateway>,
|
||||
/// Caches the existence of internet resource
|
||||
internet_resource_enabled: Option<ResourceId>,
|
||||
@@ -51,11 +53,13 @@ impl ClientOnGateway {
|
||||
id: ClientId,
|
||||
client_tun: IpConfig,
|
||||
gateway_tun: IpConfig,
|
||||
flow_properties: flow_tracker::ClientProperties,
|
||||
) -> ClientOnGateway {
|
||||
ClientOnGateway {
|
||||
id,
|
||||
client_tun,
|
||||
gateway_tun,
|
||||
flow_properties,
|
||||
resources: BTreeMap::new(),
|
||||
filters: IpNetworkTable::new(),
|
||||
permanent_translations: Default::default(),
|
||||
@@ -568,6 +572,10 @@ impl ClientOnGateway {
|
||||
pub fn id(&self) -> ClientId {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn client_flow_properties(&self) -> flow_tracker::ClientProperties {
|
||||
self.flow_properties.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -757,7 +765,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn gateway_filters_expire_individually() {
|
||||
let mut peer = ClientOnGateway::new(client_id(), client_tun(), gateway_tun());
|
||||
let mut peer = ClientOnGateway::new(
|
||||
client_id(),
|
||||
client_tun(),
|
||||
gateway_tun(),
|
||||
flow_tracker::ClientProperties::default(),
|
||||
);
|
||||
let now = Utc::now();
|
||||
let then = now + Duration::from_secs(10);
|
||||
let after_then = then + Duration::from_secs(10);
|
||||
@@ -841,7 +854,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn allows_packets_for_and_from_gateway_tun_ip() {
|
||||
let mut peer = ClientOnGateway::new(client_id(), client_tun(), gateway_tun());
|
||||
let mut peer = ClientOnGateway::new(
|
||||
client_id(),
|
||||
client_tun(),
|
||||
gateway_tun(),
|
||||
flow_tracker::ClientProperties::default(),
|
||||
);
|
||||
|
||||
let request = ip_packet::make::tcp_packet(
|
||||
client_tun_ipv4(),
|
||||
@@ -876,7 +894,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn dns_and_cidr_filters_dot_mix() {
|
||||
let mut peer = ClientOnGateway::new(client_id(), client_tun(), gateway_tun());
|
||||
let mut peer = ClientOnGateway::new(
|
||||
client_id(),
|
||||
client_tun(),
|
||||
gateway_tun(),
|
||||
flow_tracker::ClientProperties::default(),
|
||||
);
|
||||
peer.add_resource(foo_dns_resource(), None);
|
||||
peer.add_resource(bar_cidr_resource(), None);
|
||||
peer.setup_nat(
|
||||
@@ -942,7 +965,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn internet_resource_doesnt_allow_all_traffic_for_dns_resources() {
|
||||
let mut peer = ClientOnGateway::new(client_id(), client_tun(), gateway_tun());
|
||||
let mut peer = ClientOnGateway::new(
|
||||
client_id(),
|
||||
client_tun(),
|
||||
gateway_tun(),
|
||||
flow_tracker::ClientProperties::default(),
|
||||
);
|
||||
peer.add_resource(foo_dns_resource(), None);
|
||||
peer.add_resource(internet_resource(), None);
|
||||
peer.setup_nat(
|
||||
@@ -994,7 +1022,12 @@ mod tests {
|
||||
fn dns_resource_packet_is_dropped_after_nat_session_expires() {
|
||||
let _guard = firezone_logging::test("trace");
|
||||
|
||||
let mut peer = ClientOnGateway::new(client_id(), client_tun(), gateway_tun());
|
||||
let mut peer = ClientOnGateway::new(
|
||||
client_id(),
|
||||
client_tun(),
|
||||
gateway_tun(),
|
||||
flow_tracker::ClientProperties::default(),
|
||||
);
|
||||
peer.add_resource(foo_dns_resource(), None);
|
||||
peer.setup_nat(
|
||||
foo_name().parse().unwrap(),
|
||||
@@ -1061,7 +1094,12 @@ mod tests {
|
||||
|
||||
let now = Instant::now();
|
||||
|
||||
let mut peer = ClientOnGateway::new(client_id(), client_tun(), gateway_tun());
|
||||
let mut peer = ClientOnGateway::new(
|
||||
client_id(),
|
||||
client_tun(),
|
||||
gateway_tun(),
|
||||
flow_tracker::ClientProperties::default(),
|
||||
);
|
||||
peer.add_resource(foo_dns_resource(), None);
|
||||
peer.setup_nat(
|
||||
foo_name().parse().unwrap(),
|
||||
@@ -1116,7 +1154,12 @@ mod tests {
|
||||
|
||||
let now = Instant::now();
|
||||
|
||||
let mut peer = ClientOnGateway::new(client_id(), client_tun(), gateway_tun());
|
||||
let mut peer = ClientOnGateway::new(
|
||||
client_id(),
|
||||
client_tun(),
|
||||
gateway_tun(),
|
||||
flow_tracker::ClientProperties::default(),
|
||||
);
|
||||
peer.add_resource(foo_dns_resource(), None);
|
||||
peer.add_resource(baz_dns_resource(), None);
|
||||
peer.setup_nat(
|
||||
@@ -1345,6 +1388,7 @@ mod proptests {
|
||||
v6: client_v6,
|
||||
},
|
||||
gateway_tun(),
|
||||
flow_tracker::ClientProperties::default(),
|
||||
);
|
||||
for (resource, _, _) in &resources {
|
||||
peer.add_resource(resource.clone(), None);
|
||||
@@ -1403,6 +1447,7 @@ mod proptests {
|
||||
v6: client_v6,
|
||||
},
|
||||
gateway_tun(),
|
||||
flow_tracker::ClientProperties::default(),
|
||||
);
|
||||
|
||||
for ((filters, _), resource_id) in std::iter::zip(&protocol_config, resources_ids) {
|
||||
@@ -1466,6 +1511,7 @@ mod proptests {
|
||||
v6: client_v6,
|
||||
},
|
||||
gateway_tun(),
|
||||
flow_tracker::ClientProperties::default(),
|
||||
);
|
||||
let packet = match protocol {
|
||||
Protocol::Tcp { dport } => {
|
||||
@@ -1523,6 +1569,7 @@ mod proptests {
|
||||
v6: client_v6,
|
||||
},
|
||||
gateway_tun(),
|
||||
flow_tracker::ClientProperties::default(),
|
||||
);
|
||||
|
||||
let packet_allowed = match protocol_allowed {
|
||||
|
||||
@@ -33,6 +33,22 @@ pub struct FlowTracker {
|
||||
created_at_utc: DateTime<Utc>,
|
||||
}
|
||||
|
||||
/// Additional properties we track for a client.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ClientProperties {
|
||||
pub version: Option<String>,
|
||||
pub device_serial: Option<String>,
|
||||
pub device_uuid: Option<String>,
|
||||
pub device_os_name: Option<String>,
|
||||
pub device_os_version: Option<String>,
|
||||
pub identifier_for_vendor: Option<String>,
|
||||
pub firebase_installation_id: Option<String>,
|
||||
pub identity_id: Option<String>,
|
||||
pub identity_name: Option<String>,
|
||||
pub actor_id: Option<String>,
|
||||
pub actor_email: Option<String>,
|
||||
}
|
||||
|
||||
impl FlowTracker {
|
||||
pub fn new(now: Instant) -> Self {
|
||||
Self {
|
||||
@@ -182,7 +198,7 @@ impl FlowTracker {
|
||||
match (src_proto, dst_proto) {
|
||||
(Protocol::Tcp(src_port), Protocol::Tcp(dst_port)) => {
|
||||
let key = TcpFlowKey {
|
||||
client,
|
||||
client: client.id,
|
||||
resource: resource.id,
|
||||
src_ip,
|
||||
dst_ip,
|
||||
@@ -209,6 +225,17 @@ impl FlowTracker {
|
||||
domain,
|
||||
resource_name: resource.name,
|
||||
resource_address: resource.address,
|
||||
client_version: client.version,
|
||||
device_os_name: client.device_os_name,
|
||||
device_os_version: client.device_os_version,
|
||||
device_serial: client.device_serial,
|
||||
device_uuid: client.device_uuid,
|
||||
identifier_for_vendor: client.identifier_for_vendor,
|
||||
firebase_installation_id: client.firebase_installation_id,
|
||||
actor_id: client.actor_id,
|
||||
actor_email: client.actor_email,
|
||||
identity_id: client.identity_id,
|
||||
identity_name: client.identity_name,
|
||||
});
|
||||
}
|
||||
hash_map::Entry::Occupied(occupied) if occupied.get().context != context => {
|
||||
@@ -237,6 +264,17 @@ impl FlowTracker {
|
||||
domain,
|
||||
resource_name: resource.name,
|
||||
resource_address: resource.address,
|
||||
client_version: client.version,
|
||||
device_os_name: client.device_os_name,
|
||||
device_os_version: client.device_os_version,
|
||||
device_serial: client.device_serial,
|
||||
device_uuid: client.device_uuid,
|
||||
identifier_for_vendor: client.identifier_for_vendor,
|
||||
firebase_installation_id: client.firebase_installation_id,
|
||||
actor_id: client.actor_id,
|
||||
actor_email: client.actor_email,
|
||||
identity_id: client.identity_id,
|
||||
identity_name: client.identity_name,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -261,6 +299,17 @@ impl FlowTracker {
|
||||
domain,
|
||||
resource_name: resource.name,
|
||||
resource_address: resource.address,
|
||||
client_version: client.version,
|
||||
device_os_name: client.device_os_name,
|
||||
device_os_version: client.device_os_version,
|
||||
device_serial: client.device_serial,
|
||||
device_uuid: client.device_uuid,
|
||||
identifier_for_vendor: client.identifier_for_vendor,
|
||||
firebase_installation_id: client.firebase_installation_id,
|
||||
actor_id: client.actor_id,
|
||||
actor_email: client.actor_email,
|
||||
identity_id: client.identity_id,
|
||||
identity_name: client.identity_name,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -286,7 +335,7 @@ impl FlowTracker {
|
||||
}
|
||||
(Protocol::Udp(src_port), Protocol::Udp(dst_port)) => {
|
||||
let key = UdpFlowKey {
|
||||
client,
|
||||
client: client.id,
|
||||
resource: resource.id,
|
||||
src_ip,
|
||||
dst_ip,
|
||||
@@ -306,6 +355,17 @@ impl FlowTracker {
|
||||
domain,
|
||||
resource_name: resource.name,
|
||||
resource_address: resource.address,
|
||||
client_version: client.version,
|
||||
device_os_name: client.device_os_name,
|
||||
device_os_version: client.device_os_version,
|
||||
device_serial: client.device_serial,
|
||||
device_uuid: client.device_uuid,
|
||||
identifier_for_vendor: client.identifier_for_vendor,
|
||||
firebase_installation_id: client.firebase_installation_id,
|
||||
actor_id: client.actor_id,
|
||||
actor_email: client.actor_email,
|
||||
identity_id: client.identity_id,
|
||||
identity_name: client.identity_name,
|
||||
});
|
||||
}
|
||||
hash_map::Entry::Occupied(occupied) if occupied.get().context != context => {
|
||||
@@ -332,6 +392,17 @@ impl FlowTracker {
|
||||
domain,
|
||||
resource_name: value.resource_name,
|
||||
resource_address: value.resource_address,
|
||||
client_version: client.version,
|
||||
device_os_name: client.device_os_name,
|
||||
device_os_version: client.device_os_version,
|
||||
device_serial: client.device_serial,
|
||||
device_uuid: client.device_uuid,
|
||||
identifier_for_vendor: client.identifier_for_vendor,
|
||||
firebase_installation_id: client.firebase_installation_id,
|
||||
actor_id: client.actor_id,
|
||||
actor_email: client.actor_email,
|
||||
identity_id: client.identity_id,
|
||||
identity_name: client.identity_name,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -457,7 +528,20 @@ pub enum CompletedFlow {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CompletedTcpFlow {
|
||||
pub client: ClientId,
|
||||
pub client_id: ClientId,
|
||||
pub client_version: Option<String>,
|
||||
|
||||
pub device_os_name: Option<String>,
|
||||
pub device_os_version: Option<String>,
|
||||
pub device_serial: Option<String>,
|
||||
pub device_uuid: Option<String>,
|
||||
pub device_identifier_for_vendor: Option<String>,
|
||||
pub device_firebase_installation_id: Option<String>,
|
||||
|
||||
pub identity_id: Option<String>,
|
||||
pub identity_name: Option<String>,
|
||||
pub actor_id: Option<String>,
|
||||
pub actor_email: Option<String>,
|
||||
|
||||
pub resource_id: ResourceId,
|
||||
pub resource_name: String,
|
||||
@@ -486,7 +570,20 @@ pub struct CompletedTcpFlow {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CompletedUdpFlow {
|
||||
pub client: ClientId,
|
||||
pub client_id: ClientId,
|
||||
pub client_version: Option<String>,
|
||||
|
||||
pub device_os_name: Option<String>,
|
||||
pub device_os_version: Option<String>,
|
||||
pub device_serial: Option<String>,
|
||||
pub device_uuid: Option<String>,
|
||||
pub device_identifier_for_vendor: Option<String>,
|
||||
pub device_firebase_installation_id: Option<String>,
|
||||
|
||||
pub identity_id: Option<String>,
|
||||
pub identity_name: Option<String>,
|
||||
pub actor_id: Option<String>,
|
||||
pub actor_email: Option<String>,
|
||||
|
||||
pub resource_id: ResourceId,
|
||||
pub resource_name: String,
|
||||
@@ -516,7 +613,18 @@ pub struct CompletedUdpFlow {
|
||||
impl CompletedTcpFlow {
|
||||
fn new(key: TcpFlowKey, value: TcpFlowValue, end: DateTime<Utc>) -> Self {
|
||||
Self {
|
||||
client: key.client,
|
||||
client_id: key.client,
|
||||
client_version: value.client_version,
|
||||
device_os_name: value.device_os_name,
|
||||
device_os_version: value.device_os_version,
|
||||
device_serial: value.device_serial,
|
||||
device_uuid: value.device_uuid,
|
||||
device_identifier_for_vendor: value.identifier_for_vendor,
|
||||
device_firebase_installation_id: value.firebase_installation_id,
|
||||
actor_id: value.actor_id,
|
||||
actor_email: value.actor_email,
|
||||
identity_id: value.identity_id,
|
||||
identity_name: value.identity_name,
|
||||
resource_id: key.resource,
|
||||
resource_name: value.resource_name,
|
||||
resource_address: value.resource_address,
|
||||
@@ -543,7 +651,18 @@ impl CompletedTcpFlow {
|
||||
impl CompletedUdpFlow {
|
||||
fn new(key: UdpFlowKey, value: UdpFlowValue, end: DateTime<Utc>) -> Self {
|
||||
Self {
|
||||
client: key.client,
|
||||
client_id: key.client,
|
||||
client_version: value.client_version,
|
||||
device_os_name: value.device_os_name,
|
||||
device_os_version: value.device_os_version,
|
||||
device_serial: value.device_serial,
|
||||
device_uuid: value.device_uuid,
|
||||
device_identifier_for_vendor: value.identifier_for_vendor,
|
||||
device_firebase_installation_id: value.firebase_installation_id,
|
||||
actor_id: value.actor_id,
|
||||
actor_email: value.actor_email,
|
||||
identity_id: value.identity_id,
|
||||
identity_name: value.identity_name,
|
||||
resource_id: key.resource,
|
||||
resource_name: value.resource_name,
|
||||
resource_address: value.resource_address,
|
||||
@@ -599,6 +718,19 @@ struct TcpFlowValue {
|
||||
resource_name: String,
|
||||
resource_address: String,
|
||||
|
||||
client_version: Option<String>,
|
||||
device_serial: Option<String>,
|
||||
device_uuid: Option<String>,
|
||||
device_os_name: Option<String>,
|
||||
device_os_version: Option<String>,
|
||||
identifier_for_vendor: Option<String>,
|
||||
firebase_installation_id: Option<String>,
|
||||
|
||||
identity_id: Option<String>,
|
||||
identity_name: Option<String>,
|
||||
actor_id: Option<String>,
|
||||
actor_email: Option<String>,
|
||||
|
||||
fin_tx: bool,
|
||||
fin_rx: bool,
|
||||
}
|
||||
@@ -614,6 +746,19 @@ struct UdpFlowValue {
|
||||
|
||||
resource_name: String,
|
||||
resource_address: String,
|
||||
|
||||
client_version: Option<String>,
|
||||
device_serial: Option<String>,
|
||||
device_uuid: Option<String>,
|
||||
device_os_name: Option<String>,
|
||||
device_os_version: Option<String>,
|
||||
identifier_for_vendor: Option<String>,
|
||||
firebase_installation_id: Option<String>,
|
||||
|
||||
identity_id: Option<String>,
|
||||
identity_name: Option<String>,
|
||||
actor_id: Option<String>,
|
||||
actor_email: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
@@ -708,8 +853,23 @@ pub mod inbound_wg {
|
||||
|
||||
use super::*;
|
||||
|
||||
pub fn record_client(cid: ClientId) {
|
||||
update_current_flow_inbound_wireguard(|wg| wg.client.replace(cid));
|
||||
pub fn record_client(cid: ClientId, props: ClientProperties) {
|
||||
update_current_flow_inbound_wireguard(|wg| {
|
||||
wg.client.replace(Client {
|
||||
id: cid,
|
||||
version: props.version,
|
||||
device_os_name: props.device_os_name,
|
||||
device_os_version: props.device_os_version,
|
||||
device_serial: props.device_serial,
|
||||
actor_id: props.actor_id,
|
||||
actor_email: props.actor_email,
|
||||
identity_id: props.identity_id,
|
||||
identity_name: props.identity_name,
|
||||
device_uuid: props.device_uuid,
|
||||
identifier_for_vendor: props.identifier_for_vendor,
|
||||
firebase_installation_id: props.firebase_installation_id,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
pub fn record_resource(id: ResourceId, name: String, address: String) {
|
||||
@@ -812,7 +972,7 @@ enum FlowData {
|
||||
struct InboundWireGuard {
|
||||
outer: OuterFlow<SocketAddr>,
|
||||
inner: Option<InnerFlow>,
|
||||
client: Option<ClientId>,
|
||||
client: Option<Client>,
|
||||
resource: Option<Resource>,
|
||||
/// The domain name in case this packet is for a DNS resource.
|
||||
domain: Option<DomainName>,
|
||||
@@ -847,6 +1007,26 @@ struct InnerFlow {
|
||||
payload_len: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Client {
|
||||
id: ClientId,
|
||||
|
||||
version: Option<String>,
|
||||
|
||||
device_serial: Option<String>,
|
||||
device_uuid: Option<String>,
|
||||
device_os_name: Option<String>,
|
||||
device_os_version: Option<String>,
|
||||
|
||||
identifier_for_vendor: Option<String>,
|
||||
firebase_installation_id: Option<String>,
|
||||
|
||||
identity_id: Option<String>,
|
||||
identity_name: Option<String>,
|
||||
actor_id: Option<String>,
|
||||
actor_email: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Resource {
|
||||
id: ResourceId,
|
||||
|
||||
@@ -210,6 +210,32 @@ pub struct Client {
|
||||
pub preshared_key: SecretKey,
|
||||
pub ipv4: Ipv4Addr,
|
||||
pub ipv6: Ipv6Addr,
|
||||
#[serde(default)]
|
||||
pub version: Option<String>,
|
||||
#[serde(default)]
|
||||
pub device_os_name: Option<String>,
|
||||
#[serde(default)]
|
||||
pub device_os_version: Option<String>,
|
||||
#[serde(default)]
|
||||
pub device_serial: Option<String>,
|
||||
#[serde(default)]
|
||||
pub device_uuid: Option<String>,
|
||||
#[serde(default)]
|
||||
pub identifier_for_vendor: Option<String>,
|
||||
#[serde(default)]
|
||||
pub firebase_installation_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Default)]
|
||||
pub struct Subject {
|
||||
#[serde(default)]
|
||||
pub identity_id: Option<String>,
|
||||
#[serde(default)]
|
||||
pub identity_name: Option<String>,
|
||||
#[serde(default)]
|
||||
pub actor_id: Option<String>,
|
||||
#[serde(default)]
|
||||
pub actor_email: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
@@ -220,6 +246,8 @@ pub struct AuthorizeFlow {
|
||||
pub resource: ResourceDescription,
|
||||
pub gateway_ice_credentials: IceCredentials,
|
||||
pub client: Client,
|
||||
#[serde(default)]
|
||||
pub subject: Subject,
|
||||
pub client_ice_credentials: IceCredentials,
|
||||
|
||||
#[serde(with = "ts_seconds_option")]
|
||||
|
||||
@@ -53,6 +53,12 @@ impl From<PublicKey> for Key {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Key> for PublicKey {
|
||||
fn from(value: Key) -> Self {
|
||||
value.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Key {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", Base64Display::new(&self.0, &STANDARD))
|
||||
|
||||
@@ -10,6 +10,7 @@ use super::stub_portal::StubPortal;
|
||||
use super::transition::{Destination, DnsQuery};
|
||||
use crate::client;
|
||||
use crate::dns::is_subdomain;
|
||||
use crate::messages::gateway::{Client, Subject};
|
||||
use crate::messages::{IceCredentials, Key, SecretKey};
|
||||
use crate::tests::assertions::*;
|
||||
use crate::tests::flux_capacitor::FluxCapacitor;
|
||||
@@ -823,12 +824,28 @@ impl TunnelTest {
|
||||
gateway
|
||||
.exec_mut(|g| {
|
||||
g.sut.authorize_flow(
|
||||
src,
|
||||
client_key,
|
||||
preshared_key.clone(),
|
||||
Client {
|
||||
id: src,
|
||||
public_key: client_key.into(),
|
||||
preshared_key: preshared_key.clone(),
|
||||
ipv4: self.client.inner().sut.tunnel_ip_config().unwrap().v4,
|
||||
ipv6: self.client.inner().sut.tunnel_ip_config().unwrap().v6,
|
||||
device_os_name: None,
|
||||
device_serial: None,
|
||||
device_uuid: None,
|
||||
identifier_for_vendor: None,
|
||||
firebase_installation_id: None,
|
||||
version: None,
|
||||
device_os_version: None,
|
||||
},
|
||||
Subject {
|
||||
identity_name: None,
|
||||
actor_email: None,
|
||||
identity_id: None,
|
||||
actor_id: None,
|
||||
},
|
||||
client_ice.clone(),
|
||||
gateway_ice.clone(),
|
||||
self.client.inner().sut.tunnel_ip_config().unwrap(),
|
||||
None,
|
||||
resource,
|
||||
now,
|
||||
|
||||
@@ -352,15 +352,10 @@ impl Eventloop {
|
||||
match msg {
|
||||
IngressMessages::AuthorizeFlow(msg) => {
|
||||
if let Err(snownet::NoTurnServers {}) = tunnel.state_mut().authorize_flow(
|
||||
msg.client.id,
|
||||
PublicKey::from(msg.client.public_key.0),
|
||||
msg.client.preshared_key,
|
||||
msg.client,
|
||||
msg.subject,
|
||||
msg.client_ice_credentials,
|
||||
msg.gateway_ice_credentials,
|
||||
IpConfig {
|
||||
v4: msg.client.ipv4,
|
||||
v6: msg.client.ipv6,
|
||||
},
|
||||
msg.expires_at,
|
||||
msg.resource,
|
||||
Instant::now(),
|
||||
@@ -593,6 +588,7 @@ impl Eventloop {
|
||||
v4: req.client.peer.ipv4,
|
||||
v6: req.client.peer.ipv6,
|
||||
},
|
||||
Default::default(), // Additional client properties are not supported for 1.3.x Clients and will just be empty.
|
||||
req.expires_at,
|
||||
req.resource,
|
||||
req.client
|
||||
@@ -648,6 +644,7 @@ impl Eventloop {
|
||||
v4: req.client_ipv4,
|
||||
v6: req.client_ipv6,
|
||||
},
|
||||
Default::default(), // Additional client properties are not supported for 1.3.x Clients and will just be empty.
|
||||
req.expires_at,
|
||||
req.resource,
|
||||
req.payload.map(|r| DnsResourceNatEntry::new(r, addresses)),
|
||||
|
||||
Reference in New Issue
Block a user