fix : fail to connect agent
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { execFile } from 'node:child_process';
|
import { execFile } from 'node:child_process';
|
||||||
import net from 'node:net';
|
import net from 'node:net';
|
||||||
import { promisify } from 'node:util';
|
import { promisify } from 'node:util';
|
||||||
|
import { WebSocket } from 'ws';
|
||||||
import { CONFIG_PATH, type AgentConfig } from './config.ts';
|
import { CONFIG_PATH, type AgentConfig } from './config.ts';
|
||||||
|
|
||||||
const run = promisify(execFile);
|
const run = promisify(execFile);
|
||||||
@@ -30,6 +31,67 @@ function probeTcp(host: string, port: number, timeoutMs = 4000): Promise<string
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tente la poignée de main WebSocket réelle, jeton compris.
|
||||||
|
*
|
||||||
|
* On se déconnecte sans envoyer `hello` : le serveur n'attache un agent qu'à
|
||||||
|
* réception de ce message, donc ce test ne peut pas évincer la session du
|
||||||
|
* service en cours d'exécution.
|
||||||
|
*/
|
||||||
|
function probeHandshake(
|
||||||
|
config: AgentConfig,
|
||||||
|
timeoutMs = 8000,
|
||||||
|
): Promise<{ ok: boolean; detail: string }> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
let socket: WebSocket;
|
||||||
|
try {
|
||||||
|
socket = new WebSocket(config.serverUrl, {
|
||||||
|
headers: { authorization: `Bearer ${config.token}` },
|
||||||
|
rejectUnauthorized: !config.insecureTls,
|
||||||
|
handshakeTimeout: timeoutMs,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
resolve({ ok: false, detail: err instanceof Error ? err.message : String(err) });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const finish = (result: { ok: boolean; detail: string }) => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
try {
|
||||||
|
socket.close();
|
||||||
|
} catch {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
resolve(result);
|
||||||
|
};
|
||||||
|
|
||||||
|
const timer = setTimeout(
|
||||||
|
() => finish({ ok: false, detail: 'aucune réponse à la poignée de main' }),
|
||||||
|
timeoutMs + 1000,
|
||||||
|
);
|
||||||
|
|
||||||
|
socket.once('open', () => finish({ ok: true, detail: 'jeton accepté' }));
|
||||||
|
|
||||||
|
socket.once('error', (err: Error) => {
|
||||||
|
const message = err.message;
|
||||||
|
if (message.includes('401')) {
|
||||||
|
finish({
|
||||||
|
ok: false,
|
||||||
|
detail:
|
||||||
|
'JETON REFUSÉ (401). Le serveur ne reconnaît pas ce jeton : il a été régénéré, ' +
|
||||||
|
"ou l'agent a été supprimé. Régénère-le dans le dashboard puis réinstalle avec --reset-identity.",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (message.includes('certificate') || message.includes('self-signed')) {
|
||||||
|
finish({ ok: false, detail: `certificat TLS rejeté (${message})` });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
finish({ ok: false, detail: message });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function commandExists(command: string, args: string[]): Promise<boolean> {
|
async function commandExists(command: string, args: string[]): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
await run(command, args, { timeout: 8000, windowsHide: true });
|
await run(command, args, { timeout: 8000, windowsHide: true });
|
||||||
@@ -71,12 +133,20 @@ export async function runDiagnostics(config: AgentConfig): Promise<number> {
|
|||||||
|
|
||||||
if (serverUrl) {
|
if (serverUrl) {
|
||||||
const port = Number(serverUrl.port) || (serverUrl.protocol === 'wss:' ? 443 : 80);
|
const port = Number(serverUrl.port) || (serverUrl.protocol === 'wss:' ? 443 : 80);
|
||||||
const error = await probeTcp(serverUrl.hostname, port);
|
const tcpError = await probeTcp(serverUrl.hostname, port);
|
||||||
results.push(
|
|
||||||
error
|
if (tcpError) {
|
||||||
? line('fail', 'serveur', `${serverUrl.host} injoignable (${error})`)
|
results.push(line('fail', 'serveur', `${serverUrl.host} injoignable (${tcpError})`));
|
||||||
: line('ok', 'serveur', `${serverUrl.host} joignable`),
|
} else {
|
||||||
);
|
// Le port ouvert ne prouve rien : un jeton refusé produit exactement la
|
||||||
|
// même réponse TCP. On tente donc la vraie poignée de main.
|
||||||
|
const handshake = await probeHandshake(config);
|
||||||
|
results.push(
|
||||||
|
handshake.ok
|
||||||
|
? line('ok', 'serveur', `${serverUrl.host} — jeton accepté`)
|
||||||
|
: line('fail', 'serveur', `${serverUrl.host} — ${handshake.detail}`),
|
||||||
|
);
|
||||||
|
}
|
||||||
if (serverUrl.protocol === 'ws:' && !isLoopback(serverUrl.hostname)) {
|
if (serverUrl.protocol === 'ws:' && !isLoopback(serverUrl.hostname)) {
|
||||||
line('warn', 'transport', 'ws:// non chiffré — à réserver à un réseau privé');
|
line('warn', 'transport', 'ws:// non chiffré — à réserver à un réseau privé');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user