Skip to content

Commit

Permalink
feat: Add -U option to provide a tunnelUsername to use for the init…
Browse files Browse the repository at this point in the history
…ial ssh tunnel. When not supplied, the username for the initial tunnel is the `remoteUsername`
  • Loading branch information
gkc committed Nov 22, 2023
1 parent e116787 commit b5e67b6
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 14 deletions.
6 changes: 6 additions & 0 deletions packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ class SshnpArg {
localSshOptionsArg,
verboseArg,
remoteUserNameArg,
tunnelUserNameArg,
rootDomainArg,
localSshdPortArg,
legacyDaemonArg,
Expand Down Expand Up @@ -278,6 +279,11 @@ class SshnpArg {
abbr: 'u',
help: 'username to use in the ssh session on the remote host',
);
static const tunnelUserNameArg = SshnpArg(
name: 'tunnel-user-name',
abbr: 'U',
help: 'username to use for the initial ssh tunnel',
);
static const rootDomainArg = SshnpArg(
name: 'root-domain',
help: 'atDirectory domain',
Expand Down
29 changes: 19 additions & 10 deletions packages/noports_core/lib/src/sshnp/models/sshnp_params.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class SshnpParams {
final bool sendSshPublicKey;
final List<String> localSshOptions;
final String? remoteUsername;
final String? tunnelUsername;
final bool verbose;
final String rootDomain;
final int localSshdPort;
Expand All @@ -38,7 +39,7 @@ class SshnpParams {

/// Special Arguments
final String?
profileName; // automatically populated with the filename if from a configFile
profileName; // automatically populated with the filename if from a configFile

/// Operation flags
final bool listDevices;
Expand All @@ -57,6 +58,7 @@ class SshnpParams {
this.localSshOptions = DefaultSshnpArgs.localSshOptions,
this.verbose = DefaultArgs.verbose,
this.remoteUsername,
this.tunnelUsername,
this.atKeysFilePath,
this.rootDomain = DefaultArgs.rootDomain,
this.localSshdPort = DefaultArgs.localSshdPort,
Expand Down Expand Up @@ -94,10 +96,11 @@ class SshnpParams {
atKeysFilePath: params2.atKeysFilePath ?? params1.atKeysFilePath,
identityFile: params2.identityFile ?? params1.identityFile,
identityPassphrase:
params2.identityPassphrase ?? params1.identityPassphrase,
params2.identityPassphrase ?? params1.identityPassphrase,
sendSshPublicKey: params2.sendSshPublicKey ?? params1.sendSshPublicKey,
localSshOptions: params2.localSshOptions ?? params1.localSshOptions,
remoteUsername: params2.remoteUsername ?? params1.remoteUsername,
tunnelUsername: params2.tunnelUsername ?? params1.tunnelUsername,
verbose: params2.verbose ?? params1.verbose,
rootDomain: params2.rootDomain ?? params1.rootDomain,
localSshdPort: params2.localSshdPort ?? params1.localSshdPort,
Expand All @@ -106,7 +109,7 @@ class SshnpParams {
remoteSshdPort: params2.remoteSshdPort ?? params1.remoteSshdPort,
idleTimeout: params2.idleTimeout ?? params1.idleTimeout,
addForwardsToTunnel:
params2.addForwardsToTunnel ?? params1.addForwardsToTunnel,
params2.addForwardsToTunnel ?? params1.addForwardsToTunnel,
sshClient: params2.sshClient ?? params1.sshClient,
sshAlgorithm: params2.sshAlgorithm ?? params1.sshAlgorithm,
);
Expand Down Expand Up @@ -134,11 +137,12 @@ class SshnpParams {
identityFile: partial.identityFile,
identityPassphrase: partial.identityPassphrase,
sendSshPublicKey:
partial.sendSshPublicKey ?? DefaultSshnpArgs.sendSshPublicKey,
partial.sendSshPublicKey ?? DefaultSshnpArgs.sendSshPublicKey,
localSshOptions:
partial.localSshOptions ?? DefaultSshnpArgs.localSshOptions,
partial.localSshOptions ?? DefaultSshnpArgs.localSshOptions,
verbose: partial.verbose ?? DefaultArgs.verbose,
remoteUsername: partial.remoteUsername,
tunnelUsername: partial.tunnelUsername,
atKeysFilePath: partial.atKeysFilePath,
rootDomain: partial.rootDomain ?? DefaultArgs.rootDomain,
localSshdPort: partial.localSshdPort ?? DefaultArgs.localSshdPort,
Expand All @@ -147,7 +151,7 @@ class SshnpParams {
remoteSshdPort: partial.remoteSshdPort ?? DefaultArgs.remoteSshdPort,
idleTimeout: partial.idleTimeout ?? DefaultArgs.idleTimeout,
addForwardsToTunnel:
partial.addForwardsToTunnel ?? DefaultArgs.addForwardsToTunnel,
partial.addForwardsToTunnel ?? DefaultArgs.addForwardsToTunnel,
sshClient: partial.sshClient ?? DefaultSshnpArgs.sshClient,
sshAlgorithm: partial.sshAlgorithm ?? DefaultArgs.sshAlgorithm,
);
Expand Down Expand Up @@ -190,6 +194,7 @@ class SshnpParams {
SshnpArg.sendSshPublicKeyArg.name: sendSshPublicKey,
SshnpArg.localSshOptionsArg.name: localSshOptions,
SshnpArg.remoteUserNameArg.name: remoteUsername,
SshnpArg.tunnelUserNameArg.name: tunnelUsername,
SshnpArg.verboseArg.name: verbose,
SshnpArg.rootDomainArg.name: rootDomain,
SshnpArg.localSshdPortArg.name: localSshdPort,
Expand All @@ -200,7 +205,7 @@ class SshnpParams {
SshnpArg.sshAlgorithmArg.name: sshAlgorithm.toString(),
};
args.removeWhere(
(key, value) => !parserType.shouldParse(SshnpArg.fromName(key).parseWhen),
(key, value) => !parserType.shouldParse(SshnpArg.fromName(key).parseWhen),
);
return args;
}
Expand Down Expand Up @@ -229,6 +234,7 @@ class SshnpPartialParams {
final bool? sendSshPublicKey;
final List<String>? localSshOptions;
final String? remoteUsername;
final String? tunnelUsername;
final bool? verbose;
final String? rootDomain;
final bool? legacyDaemon;
Expand All @@ -255,6 +261,7 @@ class SshnpPartialParams {
this.sendSshPublicKey,
this.localSshOptions,
this.remoteUsername,
this.tunnelUsername,
this.verbose,
this.rootDomain,
this.localSshdPort,
Expand Down Expand Up @@ -287,10 +294,11 @@ class SshnpPartialParams {
atKeysFilePath: params2.atKeysFilePath ?? params1.atKeysFilePath,
identityFile: params2.identityFile ?? params1.identityFile,
identityPassphrase:
params2.identityPassphrase ?? params1.identityPassphrase,
params2.identityPassphrase ?? params1.identityPassphrase,
sendSshPublicKey: params2.sendSshPublicKey ?? params1.sendSshPublicKey,
localSshOptions: params2.localSshOptions ?? params1.localSshOptions,
remoteUsername: params2.remoteUsername ?? params1.remoteUsername,
tunnelUsername: params2.tunnelUsername ?? params1.tunnelUsername,
verbose: params2.verbose ?? params1.verbose,
rootDomain: params2.rootDomain ?? params1.rootDomain,
localSshdPort: params2.localSshdPort ?? params1.localSshdPort,
Expand All @@ -299,7 +307,7 @@ class SshnpPartialParams {
remoteSshdPort: params2.remoteSshdPort ?? params1.remoteSshdPort,
idleTimeout: params2.idleTimeout ?? params1.idleTimeout,
addForwardsToTunnel:
params2.addForwardsToTunnel ?? params1.addForwardsToTunnel,
params2.addForwardsToTunnel ?? params1.addForwardsToTunnel,
sshClient: params2.sshClient ?? params1.sshClient,
sshAlgorithm: params2.sshAlgorithm ?? params1.sshAlgorithm,
);
Expand Down Expand Up @@ -339,6 +347,7 @@ class SshnpPartialParams {
? null
: List<String>.from(args[SshnpArg.localSshOptionsArg.name]),
remoteUsername: args[SshnpArg.remoteUserNameArg.name],
tunnelUsername: args[SshnpArg.tunnelUserNameArg.name],
verbose: args[SshnpArg.verboseArg.name],
rootDomain: args[SshnpArg.rootDomainArg.name],
localSshdPort: args[SshnpArg.localSshdPortArg.name],
Expand All @@ -353,7 +362,7 @@ class SshnpPartialParams {
sshAlgorithm: args[SshnpArg.sshAlgorithmArg.name] == null
? null
: SupportedSshAlgorithm.fromString(
args[SshnpArg.sshAlgorithmArg.name]),
args[SshnpArg.sshAlgorithmArg.name]),
);
}

Expand Down
6 changes: 6 additions & 0 deletions packages/noports_core/lib/src/sshnp/sshnp_core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ abstract class SshnpCore
/// The remote username to use for the ssh session
String? remoteUsername;

/// The username to use for the initial ssh tunnel session
String? tunnelUsername;

// * Communication Channels

/// The channel to communicate with the sshrvd (host)
Expand Down Expand Up @@ -83,6 +86,9 @@ abstract class SshnpCore
/// Set the remote username to use for the ssh session
remoteUsername = await sshnpdChannel.resolveRemoteUsername();

/// Set the username to use for the initial ssh tunnel
tunnelUsername = await sshnpdChannel.resolveTunnelUsername(remoteUsername);

/// Shares the public key if required
await sshnpdChannel.sharePublicKeyIfRequired(identityKeyPair);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,18 @@ mixin SshnpDartInitialTunnelHandler on SshnpCore
throw error;
}

var usernameForTunnel = tunnelUsername ?? getUserName(throwIfNull: true)!;
try {
AtSshKeyPair keyPair = await keyUtil.getKeyPair(identifier: identifier);
client = SSHClient(
socket,
username: remoteUsername ?? getUserName(throwIfNull: true)!,
username: usernameForTunnel,
identities: [keyPair.keyPair],
keepAliveInterval: Duration(seconds: 15),
);
} catch (e, s) {
throw SshnpError(
'Failed to create SSHClient for ${params.remoteUsername}@${sshrvdChannel.host}:${sshrvdChannel.sshrvdPort} : $e',
'Failed to create SSHClient for $usernameForTunnel@${sshrvdChannel.host}:${sshrvdChannel.sshrvdPort} : $e',
error: e,
stackTrace: s,
);
Expand All @@ -59,7 +60,7 @@ mixin SshnpDartInitialTunnelHandler on SshnpCore
await client.authenticated.catchError((e) => throw e);
} catch (e, s) {
throw SshnpError(
'Failed to authenticate as ${params.remoteUsername}@${sshrvdChannel.host}:${sshrvdChannel.sshrvdPort} : $e',
'Failed to authenticate as $usernameForTunnel@${sshrvdChannel.host}:${sshrvdChannel.sshrvdPort} : $e',
error: e,
stackTrace: s,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ mixin SshnpOpensshInitialTunnelHandler on SshnpCore
Process? process;
// If we are starting an initial tunnel, it should be to sshrvd,
// so it is safe to assume that sshrvdChannel is not null here
String argsString = '$remoteUsername@${sshrvdChannel.host}'
String argsString = '$tunnelUsername@${sshrvdChannel.host}'
' -p ${sshrvdChannel.sshrvdPort}'
' -i $identifier'
' -L $localPort:localhost:${params.remoteSshdPort}'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,17 @@ abstract class SshnpdChannel with AsyncInitialization, AtClientBindings {
}
}

/// Resolve the username to use in the initial ssh tunnel
/// If [params.tunnelUsername] is set, it will be used.
/// Otherwise, the username will be set to [remoteUsername]
Future<String?> resolveTunnelUsername(String? remoteUsername) async {
if (params.tunnelUsername != null) {
return params.tunnelUsername!;
} else {
return remoteUsername;
}
}

/// List all available devices from the daemon.
/// Returns a [SSHPNPDeviceList] object which contains a map of device names
/// and corresponding info, and a list of active devices (devices which also
Expand Down
Loading

0 comments on commit b5e67b6

Please sign in to comment.