mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
fix: jump server (#190)
This commit is contained in:
@@ -44,63 +44,78 @@ String getPrivateKey(String id) {
|
|||||||
Future<SSHClient> genClient(
|
Future<SSHClient> genClient(
|
||||||
ServerPrivateInfo spi, {
|
ServerPrivateInfo spi, {
|
||||||
void Function(GenSSHClientStatus)? onStatus,
|
void Function(GenSSHClientStatus)? onStatus,
|
||||||
|
|
||||||
|
/// Must pass this param when use multi-thread and key login
|
||||||
String? privateKey,
|
String? privateKey,
|
||||||
|
|
||||||
|
/// Must pass this param when use multi-thread and key login
|
||||||
|
String? jumpPrivateKey,
|
||||||
Duration timeout = const Duration(seconds: 5),
|
Duration timeout = const Duration(seconds: 5),
|
||||||
|
|
||||||
/// [ServerPrivateInfo] of the jump server
|
/// [ServerPrivateInfo] of the jump server
|
||||||
|
///
|
||||||
|
/// Must pass this param when use multi-thread and key login
|
||||||
ServerPrivateInfo? jumpSpi,
|
ServerPrivateInfo? jumpSpi,
|
||||||
String? jumpPrivateKey,
|
|
||||||
}) async {
|
}) async {
|
||||||
onStatus?.call(GenSSHClientStatus.socket);
|
onStatus?.call(GenSSHClientStatus.socket);
|
||||||
SSHSocket? socket;
|
|
||||||
try {
|
|
||||||
socket = await SSHSocket.connect(
|
|
||||||
spi.ip,
|
|
||||||
spi.port,
|
|
||||||
timeout: timeout,
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
if (spi.alterUrl == null) rethrow;
|
|
||||||
try {
|
|
||||||
final ipPort = spi.fromStringUrl();
|
|
||||||
socket = await SSHSocket.connect(
|
|
||||||
ipPort.ip,
|
|
||||||
ipPort.port,
|
|
||||||
timeout: timeout,
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final forward = await () async {
|
final socket = await () async {
|
||||||
if (jumpSpi != null) {
|
// Proxy
|
||||||
|
final jumpSpi_ = () {
|
||||||
|
// Multi-thread or key login
|
||||||
|
if (jumpSpi != null) return jumpSpi;
|
||||||
|
// Main thread
|
||||||
|
if (spi.jumpId != null) return Stores.server.box.get(spi.jumpId);
|
||||||
|
}();
|
||||||
|
if (jumpSpi_ != null) {
|
||||||
final jumpClient = await genClient(
|
final jumpClient = await genClient(
|
||||||
jumpSpi,
|
jumpSpi_,
|
||||||
privateKey: jumpPrivateKey,
|
privateKey: jumpPrivateKey,
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
);
|
);
|
||||||
// Use `0.0.0.0` as localhost to use all interfaces.
|
|
||||||
return await jumpClient.forwardLocal(
|
return await jumpClient.forwardLocal(
|
||||||
spi.ip,
|
spi.ip,
|
||||||
spi.port,
|
spi.port,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Direct
|
||||||
|
try {
|
||||||
|
return await SSHSocket.connect(
|
||||||
|
spi.ip,
|
||||||
|
spi.port,
|
||||||
|
timeout: timeout,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
if (spi.alterUrl == null) rethrow;
|
||||||
|
try {
|
||||||
|
final ipPort = spi.fromStringUrl();
|
||||||
|
return await SSHSocket.connect(
|
||||||
|
ipPort.ip,
|
||||||
|
ipPort.port,
|
||||||
|
timeout: timeout,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
}();
|
}();
|
||||||
|
|
||||||
if (spi.pubKeyId == null) {
|
final keyId = spi.keyId;
|
||||||
|
if (keyId == null) {
|
||||||
onStatus?.call(GenSSHClientStatus.pwd);
|
onStatus?.call(GenSSHClientStatus.pwd);
|
||||||
return SSHClient(
|
return SSHClient(
|
||||||
forward ?? socket,
|
socket,
|
||||||
username: spi.user,
|
username: spi.user,
|
||||||
onPasswordRequest: () => spi.pwd,
|
onPasswordRequest: () => spi.pwd,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
privateKey ??= getPrivateKey(spi.pubKeyId!);
|
privateKey ??= getPrivateKey(keyId);
|
||||||
|
|
||||||
onStatus?.call(GenSSHClientStatus.key);
|
onStatus?.call(GenSSHClientStatus.key);
|
||||||
return SSHClient(
|
return SSHClient(
|
||||||
forward ?? socket,
|
socket,
|
||||||
username: spi.user,
|
username: spi.user,
|
||||||
identities: await compute(loadIndentity, privateKey),
|
identities: await compute(loadIndentity, privateKey),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -18,8 +18,9 @@ class ServerPrivateInfo {
|
|||||||
final String user;
|
final String user;
|
||||||
@HiveField(4)
|
@HiveField(4)
|
||||||
final String? pwd;
|
final String? pwd;
|
||||||
|
/// [id] of private key
|
||||||
@HiveField(5)
|
@HiveField(5)
|
||||||
final String? pubKeyId;
|
final String? keyId;
|
||||||
@HiveField(6)
|
@HiveField(6)
|
||||||
final List<String>? tags;
|
final List<String>? tags;
|
||||||
@HiveField(7)
|
@HiveField(7)
|
||||||
@@ -39,7 +40,7 @@ class ServerPrivateInfo {
|
|||||||
required this.port,
|
required this.port,
|
||||||
required this.user,
|
required this.user,
|
||||||
required this.pwd,
|
required this.pwd,
|
||||||
this.pubKeyId,
|
this.keyId,
|
||||||
this.tags,
|
this.tags,
|
||||||
this.alterUrl,
|
this.alterUrl,
|
||||||
this.autoConnect,
|
this.autoConnect,
|
||||||
@@ -55,7 +56,7 @@ class ServerPrivateInfo {
|
|||||||
name = json["name"]?.toString() ??
|
name = json["name"]?.toString() ??
|
||||||
'${json["user"]?.toString() ?? 'root'}@${json["ip"].toString()}:${json["port"] ?? 22}',
|
'${json["user"]?.toString() ?? 'root'}@${json["ip"].toString()}:${json["port"] ?? 22}',
|
||||||
pwd = json["authorization"]?.toString(),
|
pwd = json["authorization"]?.toString(),
|
||||||
pubKeyId = json["pubKeyId"]?.toString(),
|
keyId = json["pubKeyId"]?.toString(),
|
||||||
tags = json["tags"]?.cast<String>(),
|
tags = json["tags"]?.cast<String>(),
|
||||||
alterUrl = json["alterUrl"]?.toString(),
|
alterUrl = json["alterUrl"]?.toString(),
|
||||||
autoConnect = json["autoConnect"],
|
autoConnect = json["autoConnect"],
|
||||||
@@ -68,7 +69,7 @@ class ServerPrivateInfo {
|
|||||||
data["port"] = port;
|
data["port"] = port;
|
||||||
data["user"] = user;
|
data["user"] = user;
|
||||||
data["authorization"] = pwd;
|
data["authorization"] = pwd;
|
||||||
data["pubKeyId"] = pubKeyId;
|
data["pubKeyId"] = keyId;
|
||||||
data["tags"] = tags;
|
data["tags"] = tags;
|
||||||
data["alterUrl"] = alterUrl;
|
data["alterUrl"] = alterUrl;
|
||||||
data["autoConnect"] = autoConnect;
|
data["autoConnect"] = autoConnect;
|
||||||
@@ -82,7 +83,7 @@ class ServerPrivateInfo {
|
|||||||
bool shouldReconnect(ServerPrivateInfo old) {
|
bool shouldReconnect(ServerPrivateInfo old) {
|
||||||
return id != old.id ||
|
return id != old.id ||
|
||||||
pwd != old.pwd ||
|
pwd != old.pwd ||
|
||||||
pubKeyId != old.pubKeyId ||
|
keyId != old.keyId ||
|
||||||
alterUrl != old.alterUrl ||
|
alterUrl != old.alterUrl ||
|
||||||
jumpId != old.jumpId;
|
jumpId != old.jumpId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class ServerPrivateInfoAdapter extends TypeAdapter<ServerPrivateInfo> {
|
|||||||
port: fields[2] as int,
|
port: fields[2] as int,
|
||||||
user: fields[3] as String,
|
user: fields[3] as String,
|
||||||
pwd: fields[4] as String?,
|
pwd: fields[4] as String?,
|
||||||
pubKeyId: fields[5] as String?,
|
keyId: fields[5] as String?,
|
||||||
tags: (fields[6] as List?)?.cast<String>(),
|
tags: (fields[6] as List?)?.cast<String>(),
|
||||||
alterUrl: fields[7] as String?,
|
alterUrl: fields[7] as String?,
|
||||||
autoConnect: fields[8] as bool?,
|
autoConnect: fields[8] as bool?,
|
||||||
@@ -45,7 +45,7 @@ class ServerPrivateInfoAdapter extends TypeAdapter<ServerPrivateInfo> {
|
|||||||
..writeByte(4)
|
..writeByte(4)
|
||||||
..write(obj.pwd)
|
..write(obj.pwd)
|
||||||
..writeByte(5)
|
..writeByte(5)
|
||||||
..write(obj.pubKeyId)
|
..write(obj.keyId)
|
||||||
..writeByte(6)
|
..writeByte(6)
|
||||||
..write(obj.tags)
|
..write(obj.tags)
|
||||||
..writeByte(7)
|
..writeByte(7)
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:toolbox/data/res/logger.dart';
|
||||||
|
import 'package:toolbox/data/res/store.dart';
|
||||||
|
|
||||||
import '../../../core/utils/server.dart';
|
import '../../../core/utils/server.dart';
|
||||||
import '../server/server_private_info.dart';
|
import '../server/server_private_info.dart';
|
||||||
import 'worker.dart';
|
import 'worker.dart';
|
||||||
@@ -10,6 +13,8 @@ class SftpReq {
|
|||||||
final String localPath;
|
final String localPath;
|
||||||
final SftpReqType type;
|
final SftpReqType type;
|
||||||
String? privateKey;
|
String? privateKey;
|
||||||
|
ServerPrivateInfo? jumpSpi;
|
||||||
|
String? jumpPrivateKey;
|
||||||
|
|
||||||
SftpReq(
|
SftpReq(
|
||||||
this.spi,
|
this.spi,
|
||||||
@@ -17,8 +22,13 @@ class SftpReq {
|
|||||||
this.localPath,
|
this.localPath,
|
||||||
this.type,
|
this.type,
|
||||||
) {
|
) {
|
||||||
if (spi.pubKeyId != null) {
|
final keyId = spi.keyId;
|
||||||
privateKey = getPrivateKey(spi.pubKeyId!);
|
if (keyId != null) {
|
||||||
|
privateKey = getPrivateKey(keyId);
|
||||||
|
}
|
||||||
|
if (spi.jumpId != null) {
|
||||||
|
jumpSpi = Stores.server.box.get(spi.jumpId);
|
||||||
|
jumpPrivateKey = Stores.key.get(jumpSpi?.keyId)?.key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,7 +93,9 @@ class SftpReqStatus {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error = Exception('sftp worker event: $event');
|
error = Exception('sftp worker event: $event');
|
||||||
|
Loggers.app.warning(error);
|
||||||
dispose();
|
dispose();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,12 @@ Future<void> _download(
|
|||||||
try {
|
try {
|
||||||
mainSendPort.send(SftpWorkerStatus.preparing);
|
mainSendPort.send(SftpWorkerStatus.preparing);
|
||||||
final watch = Stopwatch()..start();
|
final watch = Stopwatch()..start();
|
||||||
final client = await genClient(req.spi, privateKey: req.privateKey);
|
final client = await genClient(
|
||||||
|
req.spi,
|
||||||
|
privateKey: req.privateKey,
|
||||||
|
jumpSpi: req.jumpSpi,
|
||||||
|
jumpPrivateKey: req.jumpPrivateKey,
|
||||||
|
);
|
||||||
mainSendPort.send(SftpWorkerStatus.sshConnectted);
|
mainSendPort.send(SftpWorkerStatus.sshConnectted);
|
||||||
|
|
||||||
/// Create the directory if not exists
|
/// Create the directory if not exists
|
||||||
@@ -131,7 +136,12 @@ Future<void> _upload(
|
|||||||
try {
|
try {
|
||||||
mainSendPort.send(SftpWorkerStatus.preparing);
|
mainSendPort.send(SftpWorkerStatus.preparing);
|
||||||
final watch = Stopwatch()..start();
|
final watch = Stopwatch()..start();
|
||||||
final client = await genClient(req.spi, privateKey: req.privateKey);
|
final client = await genClient(
|
||||||
|
req.spi,
|
||||||
|
privateKey: req.privateKey,
|
||||||
|
jumpSpi: req.jumpSpi,
|
||||||
|
jumpPrivateKey: req.jumpPrivateKey,
|
||||||
|
);
|
||||||
mainSendPort.send(SftpWorkerStatus.sshConnectted);
|
mainSendPort.send(SftpWorkerStatus.sshConnectted);
|
||||||
|
|
||||||
final local = File(req.localPath);
|
final local = File(req.localPath);
|
||||||
|
|||||||
@@ -280,14 +280,11 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
_setServerState(s, ServerState.connecting);
|
_setServerState(s, ServerState.connecting);
|
||||||
|
|
||||||
final time1 = DateTime.now();
|
final time1 = DateTime.now();
|
||||||
final jumpSpi =
|
|
||||||
spi.jumpId == null ? null : Stores.server.box.get(spi.jumpId);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
s.client = await genClient(
|
s.client = await genClient(
|
||||||
spi,
|
spi,
|
||||||
timeout: Stores.setting.timeoutD,
|
timeout: Stores.setting.timeoutD,
|
||||||
jumpSpi: jumpSpi,
|
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_limiter.inc(sid);
|
_limiter.inc(sid);
|
||||||
@@ -304,8 +301,8 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
if (spi.jumpId == null) {
|
if (spi.jumpId == null) {
|
||||||
Loggers.app.info('Connected to ${spi.name} in $spentTime ms.');
|
Loggers.app.info('Connected to ${spi.name} in $spentTime ms.');
|
||||||
} else {
|
} else {
|
||||||
Loggers.app.info(
|
Loggers.app
|
||||||
'Connected to ${spi.name} via ${jumpSpi?.name} in $spentTime ms.');
|
.info('Connected to ${spi.name} via jump server in $spentTime ms.');
|
||||||
}
|
}
|
||||||
|
|
||||||
_setServerState(s, ServerState.connected);
|
_setServerState(s, ServerState.connected);
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
class BuildData {
|
class BuildData {
|
||||||
static const String name = "ServerBox";
|
static const String name = "ServerBox";
|
||||||
static const int build = 612;
|
static const int build = 616;
|
||||||
static const String engine = "3.13.8";
|
static const String engine = "3.13.8";
|
||||||
static const String buildAt = "2023-10-28 17:13:40";
|
static const String buildAt = "2023-10-30 14:48:00";
|
||||||
static const int modifications = 5;
|
static const int modifications = 5;
|
||||||
static const int script = 22;
|
static const int script = 23;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,31 +52,26 @@ class _ServerEditPageState extends State<ServerEditPage> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
if (widget.spi != null) {
|
final spi = widget.spi;
|
||||||
_nameController.text = widget.spi?.name ?? '';
|
if (spi != null) {
|
||||||
_ipController.text = widget.spi?.ip ?? '';
|
_nameController.text = spi.name;
|
||||||
_portController.text = (widget.spi?.port ?? 22).toString();
|
_ipController.text = spi.ip;
|
||||||
_usernameController.text = widget.spi?.user ?? '';
|
_portController.text = spi.port.toString();
|
||||||
if (widget.spi?.pubKeyId == null) {
|
_usernameController.text = spi.user;
|
||||||
_passwordController.text = widget.spi?.pwd ?? '';
|
if (spi.keyId == null) {
|
||||||
|
_passwordController.text = spi.pwd ?? '';
|
||||||
} else {
|
} else {
|
||||||
_keyIdx.value = Pros.key.pkis.indexWhere(
|
_keyIdx.value = Pros.key.pkis.indexWhere(
|
||||||
(e) => e.id == widget.spi!.pubKeyId,
|
(e) => e.id == widget.spi!.keyId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (widget.spi?.tags != null) {
|
|
||||||
/// List in dart is passed by pointer, so you need to copy it here
|
/// List in dart is passed by pointer, so you need to copy it here
|
||||||
_tags.addAll(widget.spi!.tags!);
|
_tags.addAll(spi.tags ?? []);
|
||||||
}
|
|
||||||
if (widget.spi?.alterUrl != null) {
|
_altUrlController.text = spi.alterUrl ?? '';
|
||||||
_altUrlController.text = widget.spi!.alterUrl!;
|
_autoConnect.value = spi.autoConnect ?? true;
|
||||||
}
|
_jumpServer.value = spi.jumpId;
|
||||||
if (widget.spi?.autoConnect != null) {
|
|
||||||
_autoConnect.value = widget.spi!.autoConnect!;
|
|
||||||
}
|
|
||||||
if (widget.spi?.jumpId != null) {
|
|
||||||
_jumpServer.value = widget.spi!.jumpId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -403,7 +398,7 @@ class _ServerEditPageState extends State<ServerEditPage> {
|
|||||||
port: int.parse(_portController.text),
|
port: int.parse(_portController.text),
|
||||||
user: _usernameController.text,
|
user: _usernameController.text,
|
||||||
pwd: _passwordController.text.isEmpty ? null : _passwordController.text,
|
pwd: _passwordController.text.isEmpty ? null : _passwordController.text,
|
||||||
pubKeyId: _keyIdx.value != null
|
keyId: _keyIdx.value != null
|
||||||
? Pros.key.pkis.elementAt(_keyIdx.value!).id
|
? Pros.key.pkis.elementAt(_keyIdx.value!).id
|
||||||
: null,
|
: null,
|
||||||
tags: _tags,
|
tags: _tags,
|
||||||
|
|||||||
@@ -151,18 +151,18 @@ Future<void> _gotoSSH(
|
|||||||
}
|
}
|
||||||
|
|
||||||
final path = await () async {
|
final path = await () async {
|
||||||
final tempKeyFileName = 'srvbox_pk_${spi.pubKeyId}';
|
final tempKeyFileName = 'srvbox_pk_${spi.keyId}';
|
||||||
|
|
||||||
/// For security reason, save the private key file to app doc path
|
/// For security reason, save the private key file to app doc path
|
||||||
return joinPath(await Paths.doc, tempKeyFileName);
|
return joinPath(await Paths.doc, tempKeyFileName);
|
||||||
}();
|
}();
|
||||||
final file = File(path);
|
final file = File(path);
|
||||||
final shouldGenKey = spi.pubKeyId != null;
|
final shouldGenKey = spi.keyId != null;
|
||||||
if (shouldGenKey) {
|
if (shouldGenKey) {
|
||||||
if (await file.exists()) {
|
if (await file.exists()) {
|
||||||
await file.delete();
|
await file.delete();
|
||||||
}
|
}
|
||||||
await file.writeAsString(getPrivateKey(spi.pubKeyId!));
|
await file.writeAsString(getPrivateKey(spi.keyId!));
|
||||||
extraArgs.addAll(["-i", path]);
|
extraArgs.addAll(["-i", path]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user