dartssh2
dartssh2 copied to clipboard
SSH and SFTP client written in pure Dart, aiming to be feature-rich as well as easy to use.
DartSSH 2
SSH and SFTP client written in pure Dart, aiming to be feature-rich as well as easy to use.
dartssh2 is now a complete rewrite of dartssh.
โจ Features
- Pure Dart: Working with both Dart VM and Flutter.
- SSH Session: Executing commands, spawning shells, setting environment variables, pseudo terminals, etc.
- Authentication: Supports password, private key and interactive authentication method.
- Forwarding: Supports local forwarding and remote forwarding.
- SFTP: Supports all operations defined in SFTPv3 protocol including upload, download, list, link, remove, rename, etc.
๐งฌ Built with dartssh2
ServerBox | Ssh! No Ports |
![]() ![]() |
![]() |
Feel free to add your own app here by opening a pull request.
๐งช Try
# Install the `dartssh` command.
dart pub global activate dartssh2
# Then use `dartssh` as regular `ssh` command.
dartssh [email protected]
# Example: execute a command on remote host.
dartssh [email protected] ls -al
# Example: connect to a non-standard port.
dartssh [email protected]:<port>
# Transfer files via SFTP.
dartsftp [email protected]
If the
dartssh
command can't be found after installation, you might need to set up your path.
๐ Quick start
Connect to a remote host
final client = SSHClient(
await SSHSocket.connect('localhost', 22),
username: '<username>',
onPasswordRequest: () => '<password>',
);
SSHSocket
is an interface and it's possible to implement your ownSSHSocket
if you want to use a different underlying transport rather than standard TCP socket. For example WebSocket or Unix domain socket.
Spawn a shell on remote host
final shell = await client.shell();
stdout.addStream(shell.stdout); // listening for stdout
stderr.addStream(shell.stderr); // listening for stderr
stdin.cast<Uint8List>().listen(shell.write); // writing to stdin
await shell.done; // wait for shell to exit
client.close();
Execute a command on remote host
final uptime = await client.run('uptime');
print(utf8.decode(uptime));
Ignoring stderr:
final uptime = await client.run('uptime', stderr: false);
print(utf8.decode(uptime));
client.run()
is a convenience method that wrapsclient.execute()
for running non-interactive commands.
Start a process on remote host
final session = await client.execute('cat > file.txt');
await session.stdin.addStream(File('local_file.txt').openRead().cast());
await session.stdin.close(); // Close the sink to send EOF to the remote process.
await session.done; // Wait for session to exit to ensure all data is flushed to the remote process.
print(session.exitCode); // You can get the exit code after the session is done
session.write()
is a shorthand forsession.stdin.add()
. It's recommended to usesession.stdin.addStream()
instead ofsession.write()
when you want to stream large amount of data to the remote process.
Killing a remote process by sending signal
session.kill(SSHSignal.KILL);
await session.done;
print('exitCode: ${session.exitCode}'); // -> exitCode: null
print('signal: ${session.exitSignal?.signalName}'); // -> signal: KILL
Processes killed by signals do not have an exit code, instead they have an exit signal property.
Forward connections on local port 8080 to the server
final serverSocket = await ServerSocket.bind('localhost', 8080);
await for (final socket in serverSocket) {
final forward = await client.forwardLocal('httpbin.org', 80);
forward.stream.cast<List<int>>().pipe(socket);
socket.pipe(forward.sink);
}
Forward connections to port 2222 on the server to local port 22
final forward = await client.forwardRemote(port: 2222);
if (forward == null) {
print('Failed to forward remote port');
return;
}
await for (final connection in forward.connections) {
final socket = await Socket.connect('localhost', 22);
connection.stream.cast<List<int>>().pipe(socket);
socket.pipe(connection.sink);
}
Authenticate with public keys
final client = SSHClient(
socket,
username: '<username>',
identities: [
// A single private key file may contain multiple keys.
...SSHKeyPair.fromPem(await File('path/to/id_rsa').readAsString())
],
);
Use encrypted PEM files
// Test whether the private key is encrypted.
final encrypted = SSHKeyPair.isEncrypted(await File('path/to/id_rsa').readAsString());
print(encrypted);
// If the private key is encrypted, you need to provide the passphrase.
final keys = SSHKeyPair.fromPem('<pem text>', '<passphrase>');
print(keys);
Decrypt PEM file with compute
in Flutter
List<SSHKeyPair> decryptKeyPairs(List<String> args) {
return SSHKeyPair.fromPem(args[0], args[1]);
}
final keypairs = await compute(decryptKeyPairs, ['<pem text>', '<passphrase>']);
Get the version of SSH server
await client.authenticated;
print(client.remoteVersion); // SSH-2.0-OpenSSH_7.4p1
SFTP
List remote directory
final sftp = await client.sftp();
final items = await sftp.listdir('/');
for (final item in items) {
print(item.longname);
}
Read remote file
final sftp = await client.sftp();
final file = await sftp.open('/etc/passwd');
final content = await file.readBytes();
print(latin1.decode(content));
Write remote file
final sftp = await client.sftp();
final file = await sftp.open('file.txt', mode: SftpFileOpenMode.write);
await file.writeBytes(utf8.encode('hello there!') as Uint8List);
Write at specific offset
final data = utf8.encode('world') as Uint8List
await file.writeBytes(data, offset: 6);
File upload
final sftp = await client.sftp();
final file = await sftp.open('file.txt', mode: SftpFileOpenMode.create | SftpFileOpenMode.write);
await file.write(File('local_file.txt').openRead().cast());
Clear the remote file before opening it
final file = await sftp.open('file.txt',
mode: SftpFileOpenMode.create | SftpFileOpenMode.truncate | SftpFileOpenMode.write
);
Directory operations
final sftp = await client.sftp();
await sftp.mkdir('/path/to/dir');
await sftp.rmdir('/path/to/dir');
Get/Set attributes from/to remote file/directory
await sftp.stat('/path/to/file');
await sftp.setStat(
'/path/to/file',
SftpFileAttrs(mode: SftpFileMode(userRead: true)),
);
Get the type of a remote file
final stat = await sftp.stat('/path/to/file');
print(stat.type);
// or
print(stat.isDirectory);
print(stat.isSocket);
print(stat.isSymbolicLink);
// ...
Create a link
final sftp = await client.sftp();
sftp.link('/from', '/to');
๐ช Example
SSH client:
- example/example.dart
- example/execute.dart
- example/forward_local.dart
- example/forward_remote.dart
- example/pubkey.dart
- example/shell.dart
SFTP:
- example/sftp_read.dart
- example/sftp_list.dart
- example/sftp_stat.dart
- example/sftp_upload.dart
- example/sftp_filetype.dart
๐ Supported algorithms
Host key:
-
ssh-rsa
-
rsa-sha2-[256|512]
-
ecdsa-sha2-nistp[256|384|521]
-
ssh-ed25519
Key exchange:
-
curve25519-sha256
-
ecdh-sha2-nistp[256|384|521]
-
diffie-hellman-group-exchange-sha[1|256]
-
diffie-hellman-group14-sha[1|256]
-
diffie-hellman-group1-sha1
Cipher:
-
aes[128|192|256]-ctr
-
aes[128|192|256]-cbc
Integrity:
-
hmac-md5
-
hmac-sha1
-
hmac-sha2-[256|512]
Private key:
Type | Decode | Decrypt | Encode | Encrypt |
---|---|---|---|---|
RSA | โ๏ธ | โ๏ธ | โ๏ธ | WIP |
OpenSSH RSA | โ๏ธ | โ๏ธ | โ๏ธ | WIP |
OpenSSH ECDSA | โ๏ธ | โ๏ธ | โ๏ธ | WIP |
OpenSSH Ed25519 | โ๏ธ | โ๏ธ | โ๏ธ | WIP |
โณ Roadmap
- [x] Fix broken tests
- [x] Sound null safety
- [x] Redesign API to allow starting multiple sessions.
- [x] Full SFTP
- [ ] Server
References
-
RFC 4250
The Secure Shell (SSH) Protocol Assigned Numbers -
RFC 4251
The Secure Shell (SSH) Protocol Architecture -
RFC 4252
The Secure Shell (SSH) Authentication Protocol -
RFC 4253
The Secure Shell (SSH) Transport Layer Protocol -
RFC 4254
The Secure Shell (SSH) Connection Protocol -
RFC 4255
Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints -
RFC 4256
Generic Message Exchange Authentication for the Secure Shell Protocol (SSH) -
RFC 4419
Diffie-Hellman Group Exchange for the Secure Shell (SSH) Transport Layer Protocol -
RFC 4716
The Secure Shell (SSH) Public Key File Format -
RFC 5656
Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer -
RFC 8332
Use of RSA Keys with SHA-256 and SHA-512 in the Secure Shell (SSH) Protocol -
RFC 8731
Secure Shell (SSH) Key Exchange Method Using Curve25519 and Curve448 -
draft-miller-ssh-agent-03
SSH Agent Protocol -
draft-ietf-secsh-filexfer-02
SSH File Transfer Protocol -
draft-dbider-sha2-mac-for-ssh-06
SHA-2 Data Integrity Verification for the Secure Shell (SSH) Transport Layer Protocol
Credits
https://github.com/GreenAppers/dartssh by GreenAppers
License
dartssh is released under the terms of the MIT license. See LICENSE.