@mmmbuto/nexuscrew 0.9.5 → 0.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +55 -0
- package/frontend/dist/assets/index-MdL-ZvnH.js +93 -0
- package/frontend/dist/assets/index-forYfgKZ.css +32 -0
- package/frontend/dist/index.html +2 -2
- package/frontend/dist/version.json +1 -1
- package/lib/cli/init.js +22 -2
- package/lib/cli/service.js +11 -3
- package/lib/cli/stable-alias.js +117 -0
- package/lib/nodes/pubkey-format.js +69 -0
- package/lib/nodes/tunnel-supervisor.js +14 -17
- package/lib/nodes/tunnel.js +66 -12
- package/lib/settings/pairing-coordinator.js +10 -4
- package/lib/settings/routes.js +11 -0
- package/package.json +1 -1
- package/frontend/dist/assets/index-BUbgwJsU.js +0 -93
- package/frontend/dist/assets/index-BzmGsXBy.css +0 -32
package/lib/nodes/tunnel.js
CHANGED
|
@@ -732,8 +732,37 @@ function authorizedKeysLine({ remotePort, panelRemotePort, pub } = {}) {
|
|
|
732
732
|
// Torna null se la privata non e' leggibile, e' protetta da passphrase (in
|
|
733
733
|
// batch non si puo' sbloccare) o `ssh-keygen` non c'e': senza poterla derivare
|
|
734
734
|
// non si compone nessuna riga.
|
|
735
|
-
|
|
736
|
-
|
|
735
|
+
// Esiti ENUMERATI della derivazione della pubblica (difetto strutturale
|
|
736
|
+
// registrato dall'auditor: `null | stringa` comprimeva stati DIVERSI, e il
|
|
737
|
+
// livello basso componeva frasi su fatti che non conosce — meta' della 0.9.5
|
|
738
|
+
// e' nata li'). Gli esiti sono DATI, senza testo per l'utente: le frasi si
|
|
739
|
+
// compongono al confine UI/log, mai qui. Si aggancia a P4: una chiave cifrata
|
|
740
|
+
// dava `null`, indistinguibile da «file illeggibile», e il pairing ometteva
|
|
741
|
+
// la riga in silenzio.
|
|
742
|
+
//
|
|
743
|
+
// derived — pubblica derivata; `line` e' la riga
|
|
744
|
+
// no-identity — identityFile dichiarata ma il file NON esiste
|
|
745
|
+
// actual-key-unknown — identityFile non dichiarata: la chiave che ssh
|
|
746
|
+
// usera' DAVVERO (default, agent, config) non e'
|
|
747
|
+
// sapibile da qui; nessun altro canale ripara
|
|
748
|
+
// tool-unavailable — ssh-keygen assente sulla macchina
|
|
749
|
+
// encrypted-or-unreadable — ssh-keygen non riesce sul file (cifrata,
|
|
750
|
+
// permessi, formato) o l'output non e' una riga
|
|
751
|
+
// pubblica valida
|
|
752
|
+
const PUBKEY_DERIVED = 'derived';
|
|
753
|
+
const PUBKEY_NO_IDENTITY = 'no-identity';
|
|
754
|
+
const PUBKEY_ACTUAL_KEY_UNKNOWN = 'actual-key-unknown';
|
|
755
|
+
const PUBKEY_TOOL_UNAVAILABLE = 'tool-unavailable';
|
|
756
|
+
const PUBKEY_ENCRYPTED_OR_UNREADABLE = 'encrypted-or-unreadable';
|
|
757
|
+
|
|
758
|
+
function resolvePublicKey(identityFile, impl = {}) {
|
|
759
|
+
if (!identityFile || typeof identityFile !== 'string' || identityFile.trim() === '') {
|
|
760
|
+
return { outcome: PUBKEY_ACTUAL_KEY_UNKNOWN };
|
|
761
|
+
}
|
|
762
|
+
const existsImpl = impl.existsSync || require('node:fs').existsSync;
|
|
763
|
+
if (!existsImpl(identityFile)) {
|
|
764
|
+
return { outcome: PUBKEY_NO_IDENTITY, path: identityFile };
|
|
765
|
+
}
|
|
737
766
|
const exec = impl.execImpl || require('node:child_process').execFileSync;
|
|
738
767
|
// Timeout iniettabile (test): il default 5000 ms e' la RETE, non il
|
|
739
768
|
// meccanismo — il meccanismo e' l'ambiente controllato qui sotto.
|
|
@@ -757,7 +786,7 @@ function readPublicKey(identityFile, impl = {}) {
|
|
|
757
786
|
// chiave cifrata (misurato: 0.11 s contro gli 8 s dell'attesa su tty) e
|
|
758
787
|
// non puo' lasciare helper. Un path inesistente vince su /bin/false: su
|
|
759
788
|
// Termux i binari stanno sotto $PREFIX, e un path assoluto sbagliato e'
|
|
760
|
-
// una scommessa.
|
|
789
|
+
// una scommessa; e non `setsid`, che su macOS non esiste.
|
|
761
790
|
// LIMITE DA SAPERE: SSH_ASKPASS_REQUIRE esiste da OpenSSH 8.4. Le versioni
|
|
762
791
|
// piu' vecchie lo ignorano e tornano al tty: li' il timeout qui sopra
|
|
763
792
|
// resta come rete — il degrado e' «lento», non «appeso».
|
|
@@ -770,14 +799,32 @@ function readPublicKey(identityFile, impl = {}) {
|
|
|
770
799
|
SSH_ASKPASS: path.join(process.env.HOME || os.homedir(), '.nexuscrew', 'askpass-inesistente'),
|
|
771
800
|
},
|
|
772
801
|
});
|
|
773
|
-
} catch (
|
|
802
|
+
} catch (e) {
|
|
803
|
+
// ENOENT dello SPAWN = il binario ssh-keygen non esiste: un'altra causa
|
|
804
|
+
// rispetto al file che non si deriva, e chi compone il messaggio deve
|
|
805
|
+
// poterle distinguere (prima erano lo stesso null).
|
|
806
|
+
if (e && e.code === 'ENOENT') return { outcome: PUBKEY_TOOL_UNAVAILABLE };
|
|
807
|
+
return { outcome: PUBKEY_ENCRYPTED_OR_UNREADABLE };
|
|
808
|
+
}
|
|
774
809
|
const righe = String(out || '').split('\n').filter((r) => r.trim() !== '');
|
|
775
|
-
if (righe.length !== 1) return
|
|
810
|
+
if (righe.length !== 1) return { outcome: PUBKEY_ENCRYPTED_OR_UNREADABLE };
|
|
776
811
|
const riga = righe[0].trim();
|
|
777
|
-
if (/[\u0000-\u0008\u000b-\u001f\u007f]/.test(riga))
|
|
812
|
+
if (riga.length === 0 || /[\u0000-\u0008\u000b-\u001f\u007f]/.test(riga)) {
|
|
813
|
+
return { outcome: PUBKEY_ENCRYPTED_OR_UNREADABLE };
|
|
814
|
+
}
|
|
778
815
|
const campi = riga.split(/[ \t]+/);
|
|
779
|
-
if (campi.length < 2 || !/^[A-Za-z0-9+/]+={0,2}$/.test(campi[1]))
|
|
780
|
-
|
|
816
|
+
if (campi.length < 2 || !/^[A-Za-z0-9+/]+={0,2}$/.test(campi[1])) {
|
|
817
|
+
return { outcome: PUBKEY_ENCRYPTED_OR_UNREADABLE };
|
|
818
|
+
}
|
|
819
|
+
return { outcome: PUBKEY_DERIVED, line: riga };
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// Contratto storico (supervisor e chiamanti esistenti): null | stringa.
|
|
823
|
+
// Sopra resolvePublicKey per gli esiti enumerati; questo resta per chi
|
|
824
|
+
// compone ancora sul contratto vecchio — il confine UI/log migrera'.
|
|
825
|
+
function readPublicKey(identityFile, impl = {}) {
|
|
826
|
+
const r = resolvePublicKey(identityFile, impl);
|
|
827
|
+
return r.outcome === PUBKEY_DERIVED ? r.line : null;
|
|
781
828
|
}
|
|
782
829
|
|
|
783
830
|
// La riga per UN nodo, dal nodo. Sta qui e non nella route perche' la stessa
|
|
@@ -796,14 +843,21 @@ function readPublicKey(identityFile, impl = {}) {
|
|
|
796
843
|
// provisionare un'identita' per peer — lavoro suo, non un ramo di questa
|
|
797
844
|
// funzione.
|
|
798
845
|
function authorizedKeysForNode(nodo, panelRemotePort) {
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
846
|
+
// Esito enumerato, non piu' null|stringa: chi risponde (pairing) porta il
|
|
847
|
+
// DATO fino al confine, e li' — non qui — si compone cio' che si dice.
|
|
848
|
+
const r = resolvePublicKey(nodo && nodo.identityFile);
|
|
849
|
+
if (r.outcome !== PUBKEY_DERIVED) return { outcome: r.outcome, line: null };
|
|
850
|
+
return {
|
|
851
|
+
outcome: r.outcome,
|
|
852
|
+
line: authorizedKeysLine({ remotePort: nodo.remotePort, panelRemotePort, pub: r.line }),
|
|
853
|
+
};
|
|
802
854
|
}
|
|
803
855
|
|
|
804
856
|
module.exports = {
|
|
805
857
|
SSH_BASE_OPTS,
|
|
806
|
-
authorizedKeysLine, readPublicKey, authorizedKeysForNode,
|
|
858
|
+
authorizedKeysLine, readPublicKey, resolvePublicKey, authorizedKeysForNode,
|
|
859
|
+
PUBKEY_DERIVED, PUBKEY_NO_IDENTITY, PUBKEY_ACTUAL_KEY_UNKNOWN,
|
|
860
|
+
PUBKEY_TOOL_UNAVAILABLE, PUBKEY_ENCRYPTED_OR_UNREADABLE,
|
|
807
861
|
buildForwardArgs, buildReverseArgs, backoffDelay,
|
|
808
862
|
tunnelDir, tunnelPidPath, tunnelLogPath, tunnelStatePath, readTunnelState,
|
|
809
863
|
prepareTunnelDir, openTunnelLog,
|
|
@@ -423,11 +423,17 @@ function createPairHandler(deps) {
|
|
|
423
423
|
}
|
|
424
424
|
send(res, 200, {
|
|
425
425
|
paired: true, name: b.name, instanceId: joined.instanceId, transport: 'auto', health: { status: health.status },
|
|
426
|
-
...(authorizedKeysPayload ? {
|
|
427
|
-
authorizedKeys: authorizedKeysPayload,
|
|
428
|
-
|
|
429
|
-
|
|
426
|
+
...(authorizedKeysPayload && authorizedKeysPayload.outcome === 'derived' ? {
|
|
427
|
+
authorizedKeys: authorizedKeysPayload.line,
|
|
428
|
+
// La nota si compone al confine UI/log (pubkey-format), non qui.
|
|
429
|
+
authorizedKeysNote: require('../nodes/pubkey-format.js')
|
|
430
|
+
.pubkeyPairingNote(authorizedKeysPayload, { panelPort: peerPanelPort }),
|
|
430
431
|
} : {}),
|
|
432
|
+
// Esito enumerato come DATO (resolver a cinque esiti): prima una
|
|
433
|
+
// chiave cifrata era indistinguibile da «file illeggibile» e la riga
|
|
434
|
+
// spariva in silenzio. Il confine UI/log compone le frasi su questo;
|
|
435
|
+
// qui si porta il fatto, non il testo.
|
|
436
|
+
...(authorizedKeysPayload ? { authorizedKeysOutcome: authorizedKeysPayload.outcome } : {}),
|
|
431
437
|
});
|
|
432
438
|
} catch (e) {
|
|
433
439
|
await rollback();
|
package/lib/settings/routes.js
CHANGED
|
@@ -57,6 +57,7 @@ const {
|
|
|
57
57
|
} = require('../proxy/federation.js');
|
|
58
58
|
const { rotateToken } = require('../auth/token.js');
|
|
59
59
|
const { generateService, installService, installPath: svcInstallPath } = require('../cli/service.js');
|
|
60
|
+
const { resolveBootPaths } = require('../cli/stable-alias.js');
|
|
60
61
|
const { detectPlatform, nodeBin, repoRoot, uid } = require('../cli/platform.js');
|
|
61
62
|
const { isServiceRunning, readRoles, bootState } = require('../cli/commands.js');
|
|
62
63
|
const { configJsonPath } = require('../config.js');
|
|
@@ -993,6 +994,15 @@ function settingsRoutes(deps = {}) {
|
|
|
993
994
|
uid: seams.uid || uid(),
|
|
994
995
|
installPath: seams.serviceInstallPath,
|
|
995
996
|
};
|
|
997
|
+
// 1-bis (R23): come in init — il path del boot si risolve alla
|
|
998
|
+
// scrittura e cio' che non ha alias stabile torna DICHIARATO. Le
|
|
999
|
+
// warnings sono DATI nella risposta, non testo composto qui.
|
|
1000
|
+
const svcBoot = resolveBootPaths({
|
|
1001
|
+
nodeBin: ctx.nodeBin,
|
|
1002
|
+
entryPath: path.join(ctx.repoRoot, 'bin', 'nexuscrew.js'),
|
|
1003
|
+
});
|
|
1004
|
+
ctx.nodeBin = svcBoot.nodeBin;
|
|
1005
|
+
ctx.entryPath = svcBoot.entryPath;
|
|
996
1006
|
const content = generateService(platform, ctx);
|
|
997
1007
|
const out = installService(platform, content, ctx, { activate: false });
|
|
998
1008
|
send(res, 200, {
|
|
@@ -1000,6 +1010,7 @@ function settingsRoutes(deps = {}) {
|
|
|
1000
1010
|
target: out.target,
|
|
1001
1011
|
note: 'unit rigenerata; nessun restart automatico — riavvia il service per applicarla',
|
|
1002
1012
|
skippedActivation: out.skippedActivation,
|
|
1013
|
+
warnings: svcBoot.warnings,
|
|
1003
1014
|
});
|
|
1004
1015
|
} catch (e) { send(res, 500, { error: String(e.message || e) }); }
|
|
1005
1016
|
});
|