Skip to content

Commit

Permalink
fix: jump server (#190)
Browse files Browse the repository at this point in the history
  • Loading branch information
lollipopkit committed Oct 30, 2023
1 parent 4971239 commit bff799a
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 73 deletions.
73 changes: 44 additions & 29 deletions lib/core/utils/server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,63 +44,78 @@ String getPrivateKey(String id) {
Future<SSHClient> genClient(
ServerPrivateInfo spi, {
void Function(GenSSHClientStatus)? onStatus,

/// Must pass this param when use multi-thread and key login
String? privateKey,

/// Must pass this param when use multi-thread and key login
String? jumpPrivateKey,
Duration timeout = const Duration(seconds: 5),

/// [ServerPrivateInfo] of the jump server
///
/// Must pass this param when use multi-thread and key login
ServerPrivateInfo? jumpSpi,
String? jumpPrivateKey,
}) async {
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 {
if (jumpSpi != null) {
final socket = await () async {
// 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(
jumpSpi,
jumpSpi_,
privateKey: jumpPrivateKey,
timeout: timeout,
);
// Use `0.0.0.0` as localhost to use all interfaces.

return await jumpClient.forwardLocal(
spi.ip,
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);
return SSHClient(
forward ?? socket,
socket,
username: spi.user,
onPasswordRequest: () => spi.pwd,
);
}
privateKey ??= getPrivateKey(spi.pubKeyId!);
privateKey ??= getPrivateKey(keyId);

onStatus?.call(GenSSHClientStatus.key);
return SSHClient(
forward ?? socket,
socket,
username: spi.user,
identities: await compute(loadIndentity, privateKey),
);
Expand Down
11 changes: 6 additions & 5 deletions lib/data/model/server/server_private_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ class ServerPrivateInfo {
final String user;
@HiveField(4)
final String? pwd;
/// [id] of private key
@HiveField(5)
final String? pubKeyId;
final String? keyId;
@HiveField(6)
final List<String>? tags;
@HiveField(7)
Expand All @@ -39,7 +40,7 @@ class ServerPrivateInfo {
required this.port,
required this.user,
required this.pwd,
this.pubKeyId,
this.keyId,
this.tags,
this.alterUrl,
this.autoConnect,
Expand All @@ -55,7 +56,7 @@ class ServerPrivateInfo {
name = json["name"]?.toString() ??
'${json["user"]?.toString() ?? 'root'}@${json["ip"].toString()}:${json["port"] ?? 22}',
pwd = json["authorization"]?.toString(),
pubKeyId = json["pubKeyId"]?.toString(),
keyId = json["pubKeyId"]?.toString(),
tags = json["tags"]?.cast<String>(),
alterUrl = json["alterUrl"]?.toString(),
autoConnect = json["autoConnect"],
Expand All @@ -68,7 +69,7 @@ class ServerPrivateInfo {
data["port"] = port;
data["user"] = user;
data["authorization"] = pwd;
data["pubKeyId"] = pubKeyId;
data["pubKeyId"] = keyId;
data["tags"] = tags;
data["alterUrl"] = alterUrl;
data["autoConnect"] = autoConnect;
Expand All @@ -82,7 +83,7 @@ class ServerPrivateInfo {
bool shouldReconnect(ServerPrivateInfo old) {
return id != old.id ||
pwd != old.pwd ||
pubKeyId != old.pubKeyId ||
keyId != old.keyId ||
alterUrl != old.alterUrl ||
jumpId != old.jumpId;
}
Expand Down
4 changes: 2 additions & 2 deletions lib/data/model/server/server_private_info.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 14 additions & 2 deletions lib/data/model/sftp/req.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import 'dart:async';

import 'package:toolbox/data/res/logger.dart';
import 'package:toolbox/data/res/store.dart';

import '../../../core/utils/server.dart';
import '../server/server_private_info.dart';
import 'worker.dart';
Expand All @@ -10,15 +13,22 @@ class SftpReq {
final String localPath;
final SftpReqType type;
String? privateKey;
ServerPrivateInfo? jumpSpi;
String? jumpPrivateKey;

SftpReq(
this.spi,
this.remotePath,
this.localPath,
this.type,
) {
if (spi.pubKeyId != null) {
privateKey = getPrivateKey(spi.pubKeyId!);
final keyId = spi.keyId;
if (keyId != null) {
privateKey = getPrivateKey(keyId);
}
if (spi.jumpId != null) {
jumpSpi = Stores.server.box.get(spi.jumpId);
jumpPrivateKey = Stores.key.get(jumpSpi?.keyId)?.key;
}
}
}
Expand Down Expand Up @@ -83,7 +93,9 @@ class SftpReqStatus {
break;
default:
error = Exception('sftp worker event: $event');
Loggers.app.warning(error);
dispose();
break;
}
notifyListeners();
}
Expand Down
14 changes: 12 additions & 2 deletions lib/data/model/sftp/worker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,12 @@ Future<void> _download(
try {
mainSendPort.send(SftpWorkerStatus.preparing);
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);

/// Create the directory if not exists
Expand Down Expand Up @@ -131,7 +136,12 @@ Future<void> _upload(
try {
mainSendPort.send(SftpWorkerStatus.preparing);
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);

final local = File(req.localPath);
Expand Down
7 changes: 2 additions & 5 deletions lib/data/provider/server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -280,14 +280,11 @@ class ServerProvider extends ChangeNotifier {
_setServerState(s, ServerState.connecting);

final time1 = DateTime.now();
final jumpSpi =
spi.jumpId == null ? null : Stores.server.box.get(spi.jumpId);

try {
s.client = await genClient(
spi,
timeout: Stores.setting.timeoutD,
jumpSpi: jumpSpi,
);
} catch (e) {
_limiter.inc(sid);
Expand All @@ -304,8 +301,8 @@ class ServerProvider extends ChangeNotifier {
if (spi.jumpId == null) {
Loggers.app.info('Connected to ${spi.name} in $spentTime ms.');
} else {
Loggers.app.info(
'Connected to ${spi.name} via ${jumpSpi?.name} in $spentTime ms.');
Loggers.app
.info('Connected to ${spi.name} via jump server in $spentTime ms.');
}

_setServerState(s, ServerState.connected);
Expand Down
6 changes: 3 additions & 3 deletions lib/data/res/build_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

class BuildData {
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 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 script = 22;
static const int script = 23;
}
39 changes: 17 additions & 22 deletions lib/view/page/server/edit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,31 +52,26 @@ class _ServerEditPageState extends State<ServerEditPage> {
void initState() {
super.initState();

if (widget.spi != null) {
_nameController.text = widget.spi?.name ?? '';
_ipController.text = widget.spi?.ip ?? '';
_portController.text = (widget.spi?.port ?? 22).toString();
_usernameController.text = widget.spi?.user ?? '';
if (widget.spi?.pubKeyId == null) {
_passwordController.text = widget.spi?.pwd ?? '';
final spi = widget.spi;
if (spi != null) {
_nameController.text = spi.name;
_ipController.text = spi.ip;
_portController.text = spi.port.toString();
_usernameController.text = spi.user;
if (spi.keyId == null) {
_passwordController.text = spi.pwd ?? '';
} else {
_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
_tags.addAll(widget.spi!.tags!);
}
if (widget.spi?.alterUrl != null) {
_altUrlController.text = widget.spi!.alterUrl!;
}
if (widget.spi?.autoConnect != null) {
_autoConnect.value = widget.spi!.autoConnect!;
}
if (widget.spi?.jumpId != null) {
_jumpServer.value = widget.spi!.jumpId;
}

/// List in dart is passed by pointer, so you need to copy it here
_tags.addAll(spi.tags ?? []);

_altUrlController.text = spi.alterUrl ?? '';
_autoConnect.value = spi.autoConnect ?? true;
_jumpServer.value = spi.jumpId;
}
}

Expand Down Expand Up @@ -403,7 +398,7 @@ class _ServerEditPageState extends State<ServerEditPage> {
port: int.parse(_portController.text),
user: _usernameController.text,
pwd: _passwordController.text.isEmpty ? null : _passwordController.text,
pubKeyId: _keyIdx.value != null
keyId: _keyIdx.value != null
? Pros.key.pkis.elementAt(_keyIdx.value!).id
: null,
tags: _tags,
Expand Down
6 changes: 3 additions & 3 deletions lib/view/widget/server_func_btns.dart
Original file line number Diff line number Diff line change
Expand Up @@ -151,18 +151,18 @@ Future<void> _gotoSSH(
}

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
return joinPath(await Paths.doc, tempKeyFileName);
}();
final file = File(path);
final shouldGenKey = spi.pubKeyId != null;
final shouldGenKey = spi.keyId != null;
if (shouldGenKey) {
if (await file.exists()) {
await file.delete();
}
await file.writeAsString(getPrivateKey(spi.pubKeyId!));
await file.writeAsString(getPrivateKey(spi.keyId!));
extraArgs.addAll(["-i", path]);
}

Expand Down

0 comments on commit bff799a

Please sign in to comment.