From 685acdac3ab02c1178e855ad8c22db9f3e60e658 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 26 Sep 2025 00:18:36 +0000 Subject: [PATCH] feat: add more specific component type to user-agent header (#10457) In order to allow the portal to more easily classify, what kind of component is connecting, we extend the `get_user_agent` header to include a component type instead of the generic `connlib/`. --------- Signed-off-by: Thomas Eizinger Co-authored-by: Jamil --- elixir/apps/domain/lib/domain/version.ex | 5 +++ .../apps/domain/test/domain/version_test.exs | 42 +++++++++++++++++++ rust/apple-client-ffi/src/lib.rs | 6 ++- rust/client-ffi/src/lib.rs | 2 +- rust/client-ffi/src/platform/android.rs | 1 + rust/client-ffi/src/platform/fallback.rs | 1 + .../phoenix-channel/src/get_user_agent.rs | 11 ++--- rust/gateway/src/main.rs | 2 +- rust/gui-client/src-tauri/src/service.rs | 2 +- rust/headless-client/src/main.rs | 2 +- rust/relay/server/src/main.rs | 4 +- 11 files changed, 66 insertions(+), 12 deletions(-) create mode 100644 elixir/apps/domain/test/domain/version_test.exs diff --git a/elixir/apps/domain/lib/domain/version.ex b/elixir/apps/domain/lib/domain/version.ex index b86e575bf..c75a63e13 100644 --- a/elixir/apps/domain/lib/domain/version.ex +++ b/elixir/apps/domain/lib/domain/version.ex @@ -5,6 +5,11 @@ defmodule Domain.Version do |> Enum.find_value(fn "relay/" <> version -> version "connlib/" <> version -> version + "headless-client/" <> version -> version + "gui-client/" <> version -> version + "apple-client/" <> version -> version + "android-client/" <> version -> version + "gateway/" <> version -> version _ -> nil end) |> case do diff --git a/elixir/apps/domain/test/domain/version_test.exs b/elixir/apps/domain/test/domain/version_test.exs new file mode 100644 index 000000000..ecd2944d3 --- /dev/null +++ b/elixir/apps/domain/test/domain/version_test.exs @@ -0,0 +1,42 @@ +defmodule Domain.VersionTest do + use ExUnit.Case, async: true + + describe "fetch_version" do + test "can decode linux headless-client version" do + assert Domain.Version.fetch_version("Fedora/42.0.0 headless-client/1.4.5 (arm64; 24.1.0)") == + {:ok, "1.4.5"} + end + + test "can decode windows headless-client version" do + assert Domain.Version.fetch_version( + "Windows/10.0.22631 headless-client/1.4.5 (arm64; 24.1.0)" + ) == + {:ok, "1.4.5"} + end + + test "can decode apple-client version" do + assert Domain.Version.fetch_version("Mac OS/15.1.1 apple-client/1.4.5 (arm64; 24.1.0)") == + {:ok, "1.4.5"} + end + + test "can decode android-client version" do + assert Domain.Version.fetch_version("Android/14 android-client/1.4.5 (arm64; 24.1.0)") == + {:ok, "1.4.5"} + end + + test "can decode windows gui-client version" do + assert Domain.Version.fetch_version("Windows/10.0.22631 gui-client/1.4.5 (arm64; 24.1.0)") == + {:ok, "1.4.5"} + end + + test "can decode linux gui-client version" do + assert Domain.Version.fetch_version("Fedora/42.0.0 gui-client/1.4.5 (arm64; 24.1.0)") == + {:ok, "1.4.5"} + end + + test "can decode gateway version" do + assert Domain.Version.fetch_version("Fedora/42.0.0 gateway/1.4.5 (arm64; 24.1.0)") == + {:ok, "1.4.5"} + end + end +end diff --git a/rust/apple-client-ffi/src/lib.rs b/rust/apple-client-ffi/src/lib.rs index b1b483cf1..cbc515efd 100644 --- a/rust/apple-client-ffi/src/lib.rs +++ b/rust/apple-client-ffi/src/lib.rs @@ -287,7 +287,11 @@ impl WrappedSession { let portal = PhoenixChannel::disconnected( Secret::new(url), - get_user_agent(os_version_override, env!("CARGO_PKG_VERSION")), + get_user_agent( + os_version_override, + "apple-client", + env!("CARGO_PKG_VERSION"), + ), "client", (), || { diff --git a/rust/client-ffi/src/lib.rs b/rust/client-ffi/src/lib.rs index 0eb340724..df436ff54 100644 --- a/rust/client-ffi/src/lib.rs +++ b/rust/client-ffi/src/lib.rs @@ -268,7 +268,7 @@ fn connect( let portal = PhoenixChannel::disconnected( Secret::new(url), - get_user_agent(os_version, platform::VERSION), + get_user_agent(os_version, platform::COMPONENT, platform::VERSION), "client", (), || { diff --git a/rust/client-ffi/src/platform/android.rs b/rust/client-ffi/src/platform/android.rs index 53fc376b5..9db99d898 100644 --- a/rust/client-ffi/src/platform/android.rs +++ b/rust/client-ffi/src/platform/android.rs @@ -8,6 +8,7 @@ mod tun; pub const RELEASE: &str = "connlib-android@1.5.2"; // mark:next-android-version pub const VERSION: &str = "1.5.2"; +pub const COMPONENT: &str = "android-client"; /// We have valid use cases for headless Android clients /// (IoT devices, point-of-sale devices, etc), so try to reconnect for 30 days. diff --git a/rust/client-ffi/src/platform/fallback.rs b/rust/client-ffi/src/platform/fallback.rs index cfd89287b..a4721c6bf 100644 --- a/rust/client-ffi/src/platform/fallback.rs +++ b/rust/client-ffi/src/platform/fallback.rs @@ -4,6 +4,7 @@ use firezone_telemetry::Dsn; pub const RELEASE: &str = ""; pub const VERSION: &str = ""; +pub const COMPONENT: &str = ""; pub const DSN: Dsn = firezone_telemetry::TESTING; diff --git a/rust/connlib/phoenix-channel/src/get_user_agent.rs b/rust/connlib/phoenix-channel/src/get_user_agent.rs index c21f10996..d855c30f1 100644 --- a/rust/connlib/phoenix-channel/src/get_user_agent.rs +++ b/rust/connlib/phoenix-channel/src/get_user_agent.rs @@ -1,6 +1,8 @@ -pub fn get_user_agent(os_version_override: Option, app_version: &str) -> String { - const LIB_NAME: &str = "connlib"; - +pub fn get_user_agent( + os_version_override: Option, + component_name: &str, + app_version: &str, +) -> String { // Note: we could switch to sys-info and get the hostname // but we lose the arch // and neither of the libraries provide the kernel version. @@ -16,8 +18,7 @@ pub fn get_user_agent(os_version_override: Option, app_version: &str) -> let os_version = os_version_override.unwrap_or(info.version().to_string()); let additional_info = additional_info(); - let lib_name = LIB_NAME; - format!("{os_type}/{os_version} {lib_name}/{app_version}{additional_info}") + format!("{os_type}/{os_version} {component_name}/{app_version}{additional_info}") } fn additional_info() -> String { diff --git a/rust/gateway/src/main.rs b/rust/gateway/src/main.rs index 167fa920d..caf674652 100644 --- a/rust/gateway/src/main.rs +++ b/rust/gateway/src/main.rs @@ -182,7 +182,7 @@ async fn try_main(cli: Cli, telemetry: &mut Telemetry) -> Result<()> { ); let portal = PhoenixChannel::disconnected( Secret::new(login), - get_user_agent(None, env!("CARGO_PKG_VERSION")), + get_user_agent(None, "gateway", env!("CARGO_PKG_VERSION")), PHOENIX_TOPIC, (), || { diff --git a/rust/gui-client/src-tauri/src/service.rs b/rust/gui-client/src-tauri/src/service.rs index 85f05fb6e..fb72b4573 100644 --- a/rust/gui-client/src-tauri/src/service.rs +++ b/rust/gui-client/src-tauri/src/service.rs @@ -620,7 +620,7 @@ impl<'a> Handler<'a> { // Synchronous DNS resolution here let portal = PhoenixChannel::disconnected( Secret::new(url), - get_user_agent(None, env!("CARGO_PKG_VERSION")), + get_user_agent(None, "gui-client", env!("CARGO_PKG_VERSION")), "client", (), || { diff --git a/rust/headless-client/src/main.rs b/rust/headless-client/src/main.rs index 8257c8ed5..430c50858 100644 --- a/rust/headless-client/src/main.rs +++ b/rust/headless-client/src/main.rs @@ -327,7 +327,7 @@ fn try_main() -> Result<()> { // When running interactively, it is useful for the user to see that we can't reach the portal. let portal = PhoenixChannel::disconnected( Secret::new(url), - get_user_agent(None, env!("CARGO_PKG_VERSION")), + get_user_agent(None, "headless-client", env!("CARGO_PKG_VERSION")), "client", (), move || { diff --git a/rust/relay/server/src/main.rs b/rust/relay/server/src/main.rs index 7fbe952ad..2a98f3f8c 100644 --- a/rust/relay/server/src/main.rs +++ b/rust/relay/server/src/main.rs @@ -12,7 +12,7 @@ use firezone_relay::{ }; use firezone_telemetry::{RELAY_DSN, Telemetry}; use futures::{FutureExt, future}; -use phoenix_channel::{Event, LoginUrl, NoParams, PhoenixChannel}; +use phoenix_channel::{Event, LoginUrl, NoParams, PhoenixChannel, get_user_agent}; use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; use secrecy::{ExposeSecret, Secret, SecretString}; @@ -240,7 +240,7 @@ async fn try_main(args: Args) -> Result<()> { let mut channel = PhoenixChannel::disconnected( Secret::new(login), - format!("relay/{}", env!("CARGO_PKG_VERSION")), + get_user_agent(None, "relay", env!("CARGO_PKG_VERSION")), "relay", JoinMessage { stamp_secret: server.auth_secret().expose_secret().to_string(),