diff --git a/package.json b/package.json
index 046f946..00f25df 100644
--- a/package.json
+++ b/package.json
@@ -91,7 +91,6 @@
"@types/chroma-js": "^3.1.1",
"@types/js-cookie": "^3.0.6",
"eslint-config-next": "^14.2.28",
- "cypress": "^13.13.0",
"postcss": "^8",
"prettier": "3.0.3",
"tailwindcss": "^3"
diff --git a/src/modules/peer/PeerSSHInstructions.tsx b/src/modules/peer/PeerSSHInstructions.tsx
index e425d0d..8177edf 100644
--- a/src/modules/peer/PeerSSHInstructions.tsx
+++ b/src/modules/peer/PeerSSHInstructions.tsx
@@ -39,7 +39,7 @@ export const PeerSSHInstructions = ({
icon={}
title={"Enable SSH Access"}
description={
- "Allow remote SSH access to this machine from other connected network participants. NetBird's embedded SSH server is running on port 44338."
+ "Allow remote SSH access to this machine from other connected network participants."
}
color={"netbird"}
/>
@@ -56,7 +56,7 @@ export const PeerSSHInstructions = ({
{`netbird down # if NetBird is already running`}
- {`netbird up --allow-server-ssh`}
+ {`netbird up --allow-server-ssh --enable-ssh-root`}
diff --git a/src/modules/peers/PeerVersionCell.tsx b/src/modules/peers/PeerVersionCell.tsx
index bae7edd..9e105bb 100644
--- a/src/modules/peers/PeerVersionCell.tsx
+++ b/src/modules/peers/PeerVersionCell.tsx
@@ -1,4 +1,3 @@
-import FullTooltip from "@components/FullTooltip";
import InlineLink from "@components/InlineLink";
import {
Tooltip,
@@ -8,13 +7,14 @@ import {
} from "@components/Tooltip";
import MemoizedNetBirdIcon from "@components/ui/MemoizedNetBirdIcon";
import { getOperatingSystem } from "@hooks/useOperatingSystem";
-import { parseVersionString } from "@utils/version";
+import { compareVersions } from "@utils/version";
import { ArrowRightIcon, ArrowUpCircleIcon } from "lucide-react";
import * as React from "react";
import { useMemo } from "react";
import { useApplicationContext } from "@/contexts/ApplicationProvider";
import { OperatingSystem } from "@/interfaces/OperatingSystem";
import { PeerOperatingSystemIcon } from "@/modules/peers/PeerOperatingSystemIcon";
+import FullTooltip from "@components/FullTooltip";
type Props = {
version: string;
@@ -31,7 +31,8 @@ export default function PeerVersionCell({ version, os, serial }: Props) {
operatingSystem === OperatingSystem.ANDROID
)
return false;
- return parseVersionString(version) < parseVersionString(latestVersion);
+ if (!latestVersion) return false;
+ return !compareVersions(version, latestVersion);
}, [os, version, latestVersion]);
const updateIcon = useMemo(() => {
diff --git a/src/modules/remote-access/ssh/useSSH.ts b/src/modules/remote-access/ssh/useSSH.ts
index e76922c..dfb7b36 100644
--- a/src/modules/remote-access/ssh/useSSH.ts
+++ b/src/modules/remote-access/ssh/useSSH.ts
@@ -40,10 +40,16 @@ export const useSSH = (client: any) => {
setConfig(config);
try {
+ const requiresJwt = await client.detectSSHServerType(
+ config.hostname,
+ config.port,
+ );
+
const ssh = await client.createSSHConnection(
config.hostname,
config.port,
config.username,
+ requiresJwt ? accessToken : undefined,
);
ssh.onclose = () => {
@@ -65,7 +71,7 @@ export const useSSH = (client: any) => {
return SSHStatus.DISCONNECTED;
}
},
- [client, status],
+ [client, status, accessToken],
);
const disconnect = useCallback(() => {
diff --git a/src/utils/config.ts b/src/utils/config.ts
index 4ef0db6..0928f47 100644
--- a/src/utils/config.ts
+++ b/src/utils/config.ts
@@ -68,7 +68,7 @@ const loadConfig = (): Config => {
googleAnalyticsID: configJson?.googleAnalyticsID || undefined,
googleTagManagerID: configJson?.googleTagManagerID || undefined,
wasmPath:
- configJson.wasmPath || "https://pkgs.netbird.io/wasm/client/v0.59.11",
+ configJson?.wasmPath || "https://pkgs.netbird.io/wasm/client/v0.60.0",
} as Config;
};
diff --git a/src/utils/version.test.ts b/src/utils/version.test.ts
new file mode 100644
index 0000000..ff173db
--- /dev/null
+++ b/src/utils/version.test.ts
@@ -0,0 +1,34 @@
+import { isNativeSSHSupported } from "./version.js";
+
+console.log("=== Testing isNativeSSHSupported ===");
+const sshTestCases = [
+ { version: "v0.59.9", shouldSupport: false },
+ { version: "v0.59.10", shouldSupport: false },
+ { version: "v0.59.11", shouldSupport: false },
+ { version: "v0.60.0", shouldSupport: true },
+ { version: "v0.60.1", shouldSupport: true },
+ { version: "v0.61.0", shouldSupport: true },
+ { version: "v1.0.0", shouldSupport: true },
+
+ // Edge cases
+ { version: "development", shouldSupport: true, desc: "development build" },
+ { version: "0.60.0", shouldSupport: true, desc: "no v prefix" },
+ { version: "0.59.11", shouldSupport: false, desc: "no v prefix" },
+ { version: "v0.60.0-beta", shouldSupport: true, desc: "with suffix" },
+ { version: "v0.60.0-rc1", shouldSupport: true, desc: "with rc suffix" },
+ { version: "v0.59.9-beta", shouldSupport: false, desc: "old version with suffix" },
+];
+
+let failures = 0;
+sshTestCases.forEach(({ version, shouldSupport, desc }) => {
+ const result = isNativeSSHSupported(version);
+ const status = result === shouldSupport ? "✓" : "✗";
+ if (result !== shouldSupport) failures++;
+ const label = desc ? `${version.padEnd(15)} (${desc})` : version.padEnd(15);
+ console.log(
+ `${status} ${label} → ${result.toString().padStart(5)} (expected: ${shouldSupport})`,
+ );
+});
+
+console.log(`\n${failures} test(s) failed`);
+process.exit(failures > 0 ? 1 : 0);
diff --git a/src/utils/version.ts b/src/utils/version.ts
index 0b43e49..16616e5 100644
--- a/src/utils/version.ts
+++ b/src/utils/version.ts
@@ -36,9 +36,30 @@ export const getLatestNetbirdRelease = async (
}
};
-export const parseVersionString = (version: string | undefined) => {
- if (!version) return -1;
- return parseInt(version.replace(/\D/g, ""));
+/**
+ * Compare semantic versions.
+ * Returns true if version >= minVersion.
+ */
+export const compareVersions = (
+ version: string,
+ minVersion: string,
+): boolean => {
+ const parseVersion = (v: string): number[] => {
+ return v.replace(/^v/, "").split(".").map(Number);
+ };
+
+ const vParts = parseVersion(version);
+ const minParts = parseVersion(minVersion);
+
+ for (let i = 0; i < Math.max(vParts.length, minParts.length); i++) {
+ const vPart = vParts[i] || 0;
+ const minPart = minParts[i] || 0;
+
+ if (vPart > minPart) return true;
+ if (vPart < minPart) return false;
+ }
+
+ return true;
};
/**
@@ -51,16 +72,15 @@ export const isRoutingPeerSupported = (version: string, os: string) => {
const operatingSystem = getOperatingSystem(os);
if (operatingSystem == OperatingSystem.LINUX) return true;
if (version == "development") return true;
- const versionNumber = parseVersionString(version);
- return versionNumber >= 366;
+ return compareVersions(version, "0.36.6");
};
/**
- * Check if native SSH is supported
+ * Check if native SSH is supported.
+ * Supported starting from NetBird v0.60.0+.
* @param version
*/
export const isNativeSSHSupported = (version: string) => {
if (version == "development") return true;
- const versionNumber = parseVersionString(version);
- return versionNumber >= 999999;
+ return compareVersions(version, "0.60.0");
};