Last active
August 6, 2024 13:45
-
-
Save sarensw/7016a2271b504dfd8518d7ecd1227ced to your computer and use it in GitHub Desktop.
A wrapper for the NIOSSHClient example found in the apple/swift-nio-ssh repo. It can be used to call commands on the connected ssh server.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let ssh = SwiftNioSshWrapper(host: "...", port: 22, user: "...", password: "...") | |
do { | |
try ssh.run(command: "pwd") | |
} catch { | |
print(error) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import Dispatch | |
import NIO | |
import NIOSSH | |
public class SwiftNioSshWrapper { | |
let host: String | |
let port: Int | |
let user: String | |
let password: String | |
public init(host: String, port: Int, user: String, password: String) { | |
print("Initializing the wrapper") | |
self.host = host | |
self.port = port | |
self.user = user | |
self.password = password | |
} | |
// Just prints the config | |
public func printConfig() { | |
print("Config: \(host), \(port), \(user), \(password)") | |
} | |
// This file contains an example NIO SSH client. As NIO SSH is currently under active | |
// development this file doesn't currently do all that much, but it does provide a binary you | |
// can kick off to get a feel for how NIO SSH drives the connection live. As the feature set of | |
// NIO SSH increases we'll be adding to this client to try to make it a better example of what you | |
// can do with NIO SSH. | |
final class ErrorHandler: ChannelInboundHandler { | |
typealias InboundIn = Any | |
func errorCaught(context: ChannelHandlerContext, error: Error) { | |
print("Error in pipeline: \(error)") | |
context.close(promise: nil) | |
} | |
} | |
final class AcceptAllHostKeysDelegate: NIOSSHClientServerAuthenticationDelegate { | |
func validateHostKey(hostKey: NIOSSHPublicKey, validationCompletePromise: EventLoopPromise<Void>) { | |
// Do not replicate this in your own code: validate host keys! This is a | |
// choice made for expedience, not for any other reason. | |
validationCompletePromise.succeed(()) | |
} | |
} | |
public func run(command: String) throws -> Void { | |
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) | |
defer { | |
try! group.syncShutdownGracefully() | |
} | |
let bootstrap = ClientBootstrap(group: group) | |
.channelInitializer { channel in | |
channel.pipeline.addHandlers([NIOSSHHandler(role: .client(.init(userAuthDelegate: InteractivePasswordPromptDelegate(username: self.user, password: self.password), serverAuthDelegate: AcceptAllHostKeysDelegate())), allocator: channel.allocator, inboundChildChannelInitializer: nil), ErrorHandler()]) | |
} | |
.channelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1) | |
.channelOption(ChannelOptions.socket(SocketOptionLevel(IPPROTO_TCP), TCP_NODELAY), value: 1) | |
do { | |
print("connect via ssh") | |
let channel = try bootstrap.connect(host: self.host, port: self.port).wait() | |
// We've been asked to exec. | |
print("channel created") | |
let exitStatusPromise = channel.eventLoop.makePromise(of: Int.self) | |
let childChannel: Channel = try! channel.pipeline.handler(type: NIOSSHHandler.self).flatMap { sshHandler in | |
let promise = channel.eventLoop.makePromise(of: Channel.self) | |
sshHandler.createChannel(promise) { childChannel, channelType in | |
guard channelType == .session else { | |
return channel.eventLoop.makeFailedFuture(SSHClientError.invalidChannelType) | |
} | |
return childChannel.pipeline.addHandlers([ExampleExecHandler(command: command, completePromise: exitStatusPromise), ErrorHandler()]) | |
} | |
return promise.futureResult | |
}.wait() | |
// Wait for the connection to close | |
print("closing") | |
try! childChannel.closeFuture.wait() | |
print("child channel closed") | |
let exitStatus = try! exitStatusPromise.futureResult.wait() | |
print("exist status promise awaited") | |
try! channel.close().wait() | |
print("all closed") | |
print(exitStatus) | |
// Exit like we're the command. | |
// exit(Int32(exitStatus)) | |
} catch { | |
print("could not connect via SSH") | |
return | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
import Foundation
import Dispatch
import NIO
import NIOSSH
import NIOCore
import NIOPosix
public class SshClient {
let host: String
let port: Int
// Fatal error: 'try!' expression unexpectedly raised an error: NIOCore.ChannelError.eof
}
I am also getting the error Fatal error: 'try!' expression unexpectedly raised an error: NIOCore.ChannelError.eof