Giter VIP home page Giter VIP logo

swift-nio-ssh's Introduction

SwiftNIO SSH

This project contains SSH support using SwiftNIO.

What is SwiftNIO SSH?

SwiftNIO SSH is a programmatic implementation of SSH: that is, it is a collection of APIs that allow programmers to implement SSH-speaking endpoints. Critically, this means it is more like libssh2 than openssh. SwiftNIO SSH does not ship production-ready SSH clients and servers, but instead provides the building blocks for building this kind of client and server.

There are a number of reasons to provide a programmatic SSH implementation. One is that SSH has a unique relationship to user interactivity. Technical users are highly accustomed to interacting with SSH interactively, either to run commands on remote machines or to run interactive shells. Having the ability to programmatically respond to these requests enables interesting alternative modes of interaction. As prior examples, we can point to Twisted's Manhole, which uses a programmatic SSH implementation called conch to provide an interactive Python interpreter within a running Python server, or ssh-chat, a SSH server that provides a chat room instead of regular SSH shell functionality. Innovative uses can also be imagined for TCP forwarding.

Another good reason to provide programmatic SSH is that it is not uncommon for services to need to interact with other services in a way that involves running commands. While Process solves this for the local use-case, sometimes the commands that need to be invoked are remote. While Process could launch an ssh client as a sub-process in order to run this invocation, it can be substantially more straightforward to simply invoke SSH directly. This is libssh2's target use-case. SwiftNIO SSH provides the equivalent of the networking and cryptographic layer of libssh2, allowing motivated users to drive SSH sessions directly from within Swift services.

The most recent versions of SwiftNIO SSH support Swift 5.7 and newer. The minimum Swift version supported by SwiftNIO SSH releases are detailed below:

SwiftNIO SSH Minimum Swift Version
0.0.0 ..< 0.3.0 5.1
0.3.0 ..< 0.4.0 5.2
0.4.0 ..< 0.5.0 5.4
0.5.0 ..< 0.6.2 5.5.2
0.6.2 ..< 0.9.0 5.6
0.9.0 ... 5.8

What does SwiftNIO SSH support?

SwiftNIO SSH supports SSHv2 with the following feature set:

  • All session channel features, including shell and exec channel requests
  • Direct and reverse TCP port forwarding
  • Modern cryptographic primitives only: Ed25519 and ECDSA over the major NIST curves (P256, P384, P521) for asymmetric cryptography, AES-GCM for symmetric cryptography, x25519 for key exchange
  • Password and public key user authentication
  • Supports all platforms supported by SwiftNIO and Swift Crypto

How do I use SwiftNIO SSH?

SwiftNIO SSH provides a SwiftNIO ChannelHandler, NIOSSHHandler. This handler implements the bulk of the SSH protocol directly. Users are not expected to generate SSH messages directly: instead, they interact with the NIOSSHHandler through child channels and delegates.

SSH is a multiplexed protocol: each SSH connection is subdivided into multiple bidirectional communication channels called, appropriately enough, channels. SwiftNIO SSH reflects this construction by using a "child channel" abstraction. When a peer creates a new SSH channel, SwiftNIO SSH will create a new NIO Channel that is used to represent all traffic on that SSH channel. Within this child Channel all events are strictly ordered with respect to one another: however, events in different Channels may be interleaved freely by the implementation.

An active SSH connection therefore looks like this:

┌ ─ NIO Channel ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐

│     ┌────────────────────────────────┐    │
      │                                │
│     │                                │    │
      │                                │
│     │                                │    │
      │         NIOSSHHandler          │───────────────────────┐
│     │                                │    │                  │
      │                                │                       │
│     │                                │    │                  │
      │                                │                       │
│     └────────────────────────────────┘    │                  │
                                                               │
└ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘                  │
                                                               │
                                                               │
                                                               │
                                                               │
                                                               ▼
                     ┌── SSH Child Channel ─────────────────────────────────────────────────────────────┐
                     │                                                                                  │
                     │   ┌────────────────────────────────┐      ┌────────────────────────────────┐     ├───┐
                     │   │                                │      │                                │     │   │
                     │   │                                │      │                                │     │   ├───┐
                     │   │                                │      │                                │     │   │   │
                     │   │                                │      │                                │     │   │   │
                     │   │          User Handler          │      │          User Handler          │     │   │   │
                     │   │                                │      │                                │     │   │   │
                     │   │                                │      │                                │     │   │   │
                     │   │                                │      │                                │     │   │   │
                     │   │                                │      │                                │     │   │   │
                     │   └────────────────────────────────┘      └────────────────────────────────┘     │   │   │
                     │                                                                                  │   │   │
                     └───┬──────────────────────────────────────────────────────────────────────────────┘   │   │
                         │                                                                                  │   │
                         └───┬──────────────────────────────────────────────────────────────────────────────┘   │
                             │                                                                                  │
                             └──────────────────────────────────────────────────────────────────────────────────┘

An SSH channel is invoked with a channel type. NIOSSH supports three: session, directTCPIP, and forwardedTCPIP. The most common channel type is session: session is used to represent the invocation of a program, whether a specific named program or a shell. The other two channel types are related to TCP port forwarding, and will be discussed later.

An SSH channel operates on a single data type: SSHChannelData. This structure encapsulates the fact that SSH supports both regular and "extended" channel data. The regular channel data (SSHChannelData.DataType.channel) is used for the vast majority of core data. In session channels the .channel data type is used for standard input and standard output: the .stdErr data type is used for standard error (naturally). In TCP forwarding channels, the .channel data type is the only kind used, and represents the forwarded data.

Channel Events

A session channel represents an invocation of a command. Exactly how the channel operates is communicated in a number of inbound user events. The following events are important:

  • SSHChannelRequestEvent.PseudoTerminalRequest: Requests the allocation of a pseudo-terminal.
  • SSHChannelRequestEvent.EnvironmentRequest: Requests a single environment variable for the command invocation. Always sent before the command itself.
  • SSHChannelRequestEvent.ShellRequest: Requests that the command to be invoked is the authenticated user's shell.
  • SSHChannelRequestEvent.ExecRequest: Requests the invocation of a specific command.
  • SSHChannelRequestEvent.ExitStatus: Used to signal that the remote command has exited, and communicates the exit code.
  • SSHChannelRequestEvent.ExitSignal: Used to indicate that the remote command was terminated in response to a signal, and what that signal was.
  • SSHChannelRequestEvent.SignalRequest: Used to send a signal to the remote command.
  • SSHChannelRequestEvent.LocalFlowControlRequest: Used to indicate whether the client is capable of performing Ctrl-Q/Ctrl-S flow control itself.
  • SSHChannelRequestEvent.WindowChangeRequest: Used to communicate a change in the size of the terminal window on the client to the allocated peudo-terminal.
  • SSHChannelRequestEvent.SubsystemRequest: Used to request invocation of a specific subsystem. The meaning of this is specific to individual use-cases.

These events are unused in port forwarding messages. SSH implementations that support .session type channels need to be prepared to handle most or all of these in various ways.

Each of these events also has a wantReply field. This indicates whether the request need a reply to indicate success or failure. If it does, the following two events are used:

  • ChannelSuccessEvent, to communicate success.
  • ChannelFailureEvent, to communicate failure.

Half Closure

The SSH network protocol pervasively uses half-closure in the child channels. NIO Channels typically have half-closure support disabled by default, and SwiftNIO SSH respects this default in its child channels as well. However, if you leave this setting at its default value the SSH child channels will behave extremely unexpectedly. For this reason, it is strongly recommended that all child channels have half closure support enabled:

channel.setOption(ChannelOptions.allowRemoteHalfClosure, true)

This then uses standard NIO half-closure support. The remote peer sending EOF will be communicated with an inbound user event, ChannelEvent.inputClosed. To send EOF yourself, call close(mode: .output).

User Authentication

User authentication is a vital part of SSH. To manage it, SwiftNIO SSH uses a pair of delegate protocols: NIOSSHClientUserAuthenticationDelegate and NIOSSHServerUserAuthenticationDelegate. Clients and servers should provide implementations of these delegate protocols to manage user authentication.

The client protocol is straightforward: SwiftNIO SSH will invoke the method nextAuthenticationType(availableMethods:nextChallengePromise:) on the delegate. The availableMethods will be an instance of NIOSSHAvailableUserAuthenticationMethods communicating which authentication methods the server has suggested will be acceptable. The delegate can then complete nextChallengePromise with either a new authentication request, or with nil to indicate that the client has run out of things to try.

The server protocol is more complex. The delegate must provide a supportedAuthenticationMethods property that communicates which authentication methods are supported by the delegate. Then, each time the client sends a user auth request, the requestReceived(request:responsePromise:) method will be invoked. This may be invoked multiple times in parallel, as clients are allowed to issue auth requests in parallel. The responsePromise should be succeeded with the result of the authentication. There are three results: .success and .failure are straightforward, but in principle the server can require multiple challenges using .partialSuccess(remainingMethods:).

Direct Port Forwarding

Direct port forwarding is port forwarding from client to server. In this mode traditionally the client will listen on a local port, and will forward inbound connections to the server. It will ask that the server forward these connections as outbound connections to a specific host and port.

These channels can be directly opened by clients by using the .directTCPIP channel type.

Remote Port Forwarding and Global Requests

Remote port forwarding is a less-common situation where the client asks the server to listen on a specific address and port, and to forward all inbound connections to the client. As the client needs to request this behaviour, it does so using global requests.

Global requests are initiated using NIOSSHHandler.sendGlobalRequest, and are received and handled by way of a GlobalRequestDelegate. There are two global requests supported today:

  • GlobalRequest.TCPForwardingRequest.listen(host:port:): a request for the server to listen on a given host and port.
  • GlobalRequest.TCPForwardingRequest.cancel(host:port:): a request to cancel the listening on the given host and port.

Servers may be notified of and respond to these requests using a GlobalRequestDelegate. The method to implement here is tcpForwardingRequest(_:handler:promise:). This delegate method will be invoked any time a global request is received. The response to the request is passed into promise.

Forwarded channels are then sent from server to client using the .forwardedTCPIP channel type.

swift-nio-ssh's People

Contributors

artemredkin avatar carolinacass avatar davidde94 avatar dnadoba avatar dominikhorn avatar fabianfett avatar finagolfin avatar franzbusch avatar gaetanzanella avatar glbrntt avatar gwynne avatar helje5 avatar joannis avatar lukasa avatar maartene avatar orobio avatar peteradams-a avatar rnro avatar simonjbeaumont avatar tomerd avatar weissi avatar yim-lee avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

swift-nio-ssh's Issues

Child channels can not access their own peerMaxMessageSize

There does not appear to be any way for a child channel implementation to get access to the peerMaxMessageSize value derived from the SSH_MESSAGE_CHANNEL_OPEN message. This value is needed by some subsystems, such as SFTP, which are required to manually perform splitting of large data packets manually. In SFTP's case, offering an oversized value in a WriteFile request causes some servers to enter a wedged state waiting for data that will never arrive; without access to the channel's desired maximum message size, it must assume the worst (the specification-required minimum supported size of 32768 payload bytes), which is obviously less than ideal.

iOS: upload file to sshd server

Hi
I am looking from a way to upload file from my iOS app to an external ssh server.
I have tried swift-nio-ssh (NIOSSHClient example) from macos (not yet in iOS app) and I can connet to server and execute commands such as ls. Then I tried the put command and got "bash: put: command not found"... I think even though I can solve this on macos probably iOS will not have the put command. Well, my question is:
Is it possibel to use swift-nio-ssh, from an iOS app to upload a file to a ssh server not using bash commands but using class methods, such as childChannel.xx.yy.uploadfile("./abc.txt")?

Thank you very much
Alex

There does not appear to be a way for a server to send an exit-status.

I see machinery for sending an SSH protocol "exit-status" SSHMessage, and it initializes from SSHChannelData (which I can send), but there doesn't seem to be any machinery to encode an "exit-status" in an SSHChannelData and get it through into a SSHMessage.channelRequest.

That looks to be the right way to send an exit-status, unless I'm missing something obvious.

So, is there a way I'm missing? Or maybe I should make a PR?

NIOSSHPrivateKey from file/bytes

I don't know if I am missing something, is there a way to parse a private key file or private key data with a passphrase into a NIOSSHPrivateKey without knowing the type of private key you want to open?

NIOSSHClientUserAuthenticationDelegate Improvement Request: Result Reporting

The NIOSSHClientUserAuthenticationDelegate provides a single method, nextAuthenticationRequest, for providing offers for authenticating with a remote host. In the golden path, an auth request is successful and everyone is happy. However, in situations where the request fails, either because of incorrect credentials or an unexpected failure on the remote end, it would be useful for the delegate to receive a notification to that effect. As things stand now, the only way to know an offer fails is if the method is called again. That type of inference is something I'd rather not rely upon.

In my research of this repository, I came across NIOSSHUserAuthenticationOutcome; I wonder if using that here would be helpful?

How to validate host keys (i.e. how to access information about `NIOSSHPublicKey`)?

SwiftNIO SSH commit hash: 5d95eba

Context:
I'm trying to implement a NIOSSHClientServerAuthenticationDelegate that actually verifies the host key.

The problem:
Except for debug printing, I have not found any way to access any information about the supplied NIOSSHPublicKey.

Is there currently a way to access information about a NIOSSHPublicKey? (Apologies if I missed something obvious, I'm fairly new to swift).

System Information
$ swift --version
Apple Swift version 5.3.2 (swiftlang-1200.0.45 clang-1200.0.32.28)
Target: x86_64-apple-darwin19.6.0

$ uname -a
Darwin tim-mac.local 19.6.0 Darwin Kernel Version 19.6.0: Mon Aug 31 22:12:52 PDT 2020; root:xnu-6153.141.2~1/RELEASE_X86_64 x86_64 i386 MacBookPro12,1 Darwin

Missing Direct/Forwarded TCP IP Examples

Hello,

I was playing around with the library, and while the client example is good and shows how to establish a simple SSH connection, I can't quite figure out how to configure a direct TCP connection. I was able to change the example to open a channel of directTCPIP type, but it seems to be ignoring the originatorAddress, which I am assuming would be a port on my computer (just like when I call ssh -NL) but I am probbaly wrong.

I probably am not being able to figure it out as I haven't tried Swift NIO before, but an example on those kinds of connections would be helpful to me and probably to many others. :)

Thanks in advance!

underlyingCoreCryptoError while creating NIOSSHPrivateKey

Hi,
I'm trying to setup a SSH tunnel in order to forward a remote port into my local device. I followed your PR #55 and I successfully implemented it into a brand new iOS project: I am able to forward a port using password authentication.

What I'm trying to do is performing a private key authentication. Due to the lack of the documentation, I'm going to ask you a few questions. Here's the code:

//ssh-keygen -t ecdsa -b 521 -m pem

let sshPrivateText = """
-----BEGIN EC PRIVATE KEY-----
row1
row2
row3
row4
row5
-----END EC PRIVATE KEY-----
"""
            
            let base64EncodedString:String = Data(sshPrivateText.utf8).base64EncodedString()
            let ecdsaPrivateKeyData:Data = Data(base64Encoded: base64EncodedString, options: .ignoreUnknownCharacters)!
            let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
            let key: NIOSSHPrivateKey?
            do {
                key = NIOSSHPrivateKey(p521Key: try .init(rawRepresentation: ecdsaPrivateKeyData))
            } catch let error {
                fatalError(error.localizedDescription)
            }

But I can't go further the NIOSSHPrivateKey constructor since the .init throws an error:

Fatal error: The operation couldn’t be completed. (CryptoKit.CryptoKitError error 0.): file /Users/user/XCode Projects/NioExample/NioExample/ContentView.swift, line 35
2020-12-13 12:57:48.397737+0100 NioExample[2711:69134] Fatal error: The operation couldn’t be completed. (CryptoKit.CryptoKitError error 0.): file /Users/user/XCode Projects/NioExample/NioExample/ContentView.swift, line 35
(lldb) 
▿ CryptoKitError
  ▿ underlyingCoreCryptoError : 1 element
    - error : -1

Also, I have another question. Why the minimum SDK level is iOS13? Could it be downgraded or something? It's a huge device cut! Will this project be included in cocoapods? I'm asking this because I'm going to integrate this script into a native Flutter plugin and it seems like Flutter projects are not compatibile with SwiftPM.

Thank you for this amazing project by the way!

Minimum Deployment Target

MacOS
I found 10.15 is set as minimum deployment target... is it possible to built it for 10.10 ?

ssh -L

I am trying to use the codes from main.swift to do Local Port Forwarding but I am getting::
bind(descriptor:ptr:bytes:): Operation not permitted (errno: 1)

Can someone please help to do below SSH Cmd with Swift NIO SSH::
ssh -L 4944:localhost:4944 root@__IP__ -p 5544
I sucessfully made a channel but not very sure what to do next for Port Forwarding

I'm very new to Swift Nio, found it yesterday 😅

Cleanup once NIO adopts Sendable

Once NIO adopts Sendable conformances we should cleanup the following things:

  • Remove @preconcurrency from imports
  • Remove @unchecked from SSHChildChannel conformance

NIOSSHClient gets stucked

Problem

When testing NIOSSHClient, I am always getting stucked indefinitely on childChannel wait() method. Could somebody help me understand what is the problem?

Findings

  • promise.FutureResult was created successfully, but my guess is, it was never met.
  • ExampleExecHandler was never activated and command was never executed.

Setup

  • Testing on M1 Macbook Pro with latest MacOS & XCode

Is the SSH user name accessible by the ChannelHandler?

A server may care about which user has connected. I'm not seeing a path to connect the SSH user name to any of my handler code. I'm not even seeing it saved and available in SSHChildChannel or anything hanging off of it.

Unless I've missed something this will take some doing to add. Options include:

  • Adding a SSHChannelRequestEvent with a description of the user name which is sent first.

  • NIOSSHHandler() could get a new initializer which passes authentication information into the inboundChildChannelInitializer: equivalent.

Either of these presuppose that the user name is saved somewhere.

Check inbound unprocessed packet sizes

There seem to be SSH-like servers on the internet that behave maliciously, with the intent of repelling malicious connections. They work by sending out an infinite version length or banner. I checked the recent code, but cannot find any length checks or tests. Although I doubt it's a common occurrence, I think it's best to add these checks with tests. I haven't determined a good maximum size yet, however. With some recommendations on that, I'll happily make a PR. I do think separate limits should exist for the pre/post KEX.

Third-Party Cryptographic Implementations

For implementing RSA support, which will not be landing in the main library as discussed, we'll need to support external implementations for at least public-private keys. I'd also recommend looking into the same for the symmetric encryption as seen in #48

Authentication Failed using Private Key,Public Key

Hello
We are trying to connect our Mac and aws servers using the private key. but every time we got authentication failed.
Also, we have checked both keys on another application and it's working fine.so I think the problem is in the library. can you please help?

using this method

.byPublicKeyFromFile(username: self.username, password: "", publicKey: "", privateKey: pvtfilePath?.relativePath ?? "")

but not working can you please help us

Simple SSH Client port forwarding

I am trying to figure out how to write a simple Swift code for running an ssh client port forwarding command. I am aware of [swift-nio-ssh] but am not sure how to use that in Xcode. Any suggestion?

Support OpenSSH Keys

As I use SwiftNIO SSH, I need to provide the ability for my users to employ their existing private keys to connect to a remote host. As has been well-documented, SwiftCrypto lacks the ability to decrypt such keys when generated by OpenSSH.

Quoth @Lukasa in the Slack:

"If the user’s OpenSSH private key is passphrase protected then we cannot handle them in-tree at all. Because the way those keys are encrypted does not allow us to decrypt them with the APIs Swift Crypto provides. This is a ripe opportunity for someone to write a third-party extension to the library to handle this use-case."

This proposed extension to SwiftNIO SSH should solve two orthogonal problems:

  • Support RSA, which appears to be partially solved by @Joannis' PR: #62
  • Support ECDSA and ed25519 keys, by implementing a package that can parse the key format into the appropriate raw Swift Crypto format. "Basically, anything that does have access to an AES-CBC algorithm could do this. You can construct an SSH key from, say, a P256.Signing.PrivateKey object, and that ultimately can be derived from the OpenSSH private key format," writes @Lukasa.

Crash in `SSHChildChannel`

SwiftNIO SSH commit hash: c56abab

Context

While writing Xcode tests in one of my project which uses swift-nio-ssh, I encountered a crash.
I reproduced it using the NIOSSHClient target.

  • The code is available in my fork
  • The crash happens when we force the closing of the channel while the command is running
  • The crash only appears when using a command which produces a long output such as yes \"long text\" | head -n 1000000\n
  • I only reproduced it when exchanging with the linked docker container. I'm using it in my automated tests.

Steps to reproduce

  1. Launch the ssh server: docker-compose -f ./docker/ssh-docker-compose.yaml up -d
  2. Run the modified NIOSSHClient target.
  3. Observe the crash in Xcode
* thread #2, name = 'NIO-ELT-0-#0', stop reason = Fatal error: Sent channel window adjust on channel in invalid state
    frame #0: 0x0000000193fe4ae8 libswiftCore.dylib`_swift_runtime_on_report
    frame #1: 0x00000001940825d4 libswiftCore.dylib`_swift_stdlib_reportFatalErrorInFile + 208
    frame #2: 0x0000000193c614b4 libswiftCore.dylib`closure #1 (Swift.UnsafeBufferPointer<Swift.UInt8>) -> () in closure #1 (Swift.UnsafeBufferPointer<Swift.UInt8>) -> () in Swift._assertionFailure(_: Swift.StaticString, _: Swift.String, file: Swift.StaticString, line: Swift.UInt, flags: Swift.UInt32) -> Swift.Never + 196
    frame #3: 0x0000000193c60210 libswiftCore.dylib`Swift._assertionFailure(_: Swift.StaticString, _: Swift.String, file: Swift.StaticString, line: Swift.UInt, flags: Swift.UInt32) -> Swift.Never + 308
  * frame #4: 0x00000001002cae7c NIOSSHClient`ChildChannelStateMachine.sendChannelWindowAdjust(message=(recipientChannel = 0, bytesToAdd = 8811376), self=NIOSSH.ChildChannelStateMachine @ 0x0000000101e040a8) at ChildChannelStateMachine.swift:432:13
...

Configuration

$ swift --version
swift-driver version: 1.75.2 Apple Swift version 5.8 (swiftlang-5.8.0.124.2 clang-1403.0.22.11.100)
Target: arm64-apple-macosx13.0

Operating system: macOS Ventura 13.2

Xcode version: 14.2

$ uname -a
Darwin MBP-de-Gaetan-Zanella 22.3.0 Darwin Kernel Version 22.3.0: Thu Jan 5 20:48:54 PST 2023; root:xnu-8792.81.2~2/RELEASE_ARM64_T6000 arm64

How to use Swift Nio SSH?

Today I found this project, but hit the issue that I have no clue how to use the project. I believe it would help a lot to project, if there would be some "Get started" documentation, which would just describe basic scenarios, starting with:

  • How to connect to SSH server
  • How to authenticate
  • How to run command and get result

Crypto primitives

The README says:

Modern cryptographic primitives only: Ed25519 and EDCSA over the major NIST curves (P256, P384, P521) for asymmetric cryptography, AES-GCM for symmetric cryptography, x25519 for key exchange

There's a typo: EDCSA should be ECDSA.

Also please consider support chacha20poly1305 for symmetric crypto as it performs much better for devices without hardware accelerated AES instructions and it is also the default used by modern versions of OpenSSH, the most popular SSH implementations on servers.

NIOSSHError.keyExchangeNegotiationFailure

Hello, I am trying to SSH to a HPC server using this library, but trying to authenticate when connecting always gives me the above error. I have tried both password authentication and p521 key authentication to no avail.

Since I have been able to successfully use SwiftNIO SSH to execute a test command on another known server, I am sure my code runs correctly (it is also adapted from the NIOSSHClient example code from this repository). I am also able to SSH with a regular computer to the HPC server.

Running ssh -Q key on the HPC server gives the following:

ssh-ed25519
[email protected]
ssh-rsa
ssh-dss
ecdsa-sha2-nistp256
ecdsa-sha2-nistp384
ecdsa-sha2-nistp521
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]

which is exactly the same as on the test server I have been able to connect to, and includes ed25519. Any ideas what the problem could be?

RequestSuccessMessage currently only supports opening ports

As described in #17, there are a large variety of (possible) extensions and use cases aside from opening ports on a remote machine.

The RequestSuccessMessage type needs to be modified accordingly, so that other successful replies can be successfully received and processed.

Key exchange should be initialized on handlerAdded as well

Sometimes we already have an established channel, for example,
one returned by establishing a tunneled connection. If this
is the case, then we cannot use it to start key exchange, since
it's only started when channel becomes active. In order to
support more use cases we should handle starting key exchange
when handler is added as well.

Channel closes after 1 command / Executing multiple requests on the same channel

Hi everybody,

first all of all, I want to say a huge thanks for extending SwiftNIO to support SSH connections and removing the need of relying on external libraries for ssh on swift.

I'm currently working on a ssh client that can execute requests on a remote machine. Since the project already relies on SwiftNIO, I was happy to hear about NIOSSH and wanted to use it for handling the connection and requests.
I oriented myself closely on the examples provided in NIOSSHClient and initialised the channel and child channel like this:

let clientBootstrap = ClientBootstrap(group: group)
            .channelInitializer { channel in
                channel.pipeline.addHandlers([NIOSSHHandler(role: .client(.init(userAuthDelegate: InteractivePasswordPromptDelegate(username: self.username, password: self.password), serverAuthDelegate: AcceptAllKeysDelegate())), 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)
        
        self.channel = try clientBootstrap.connect(host: self.ipAdress, port: self.port).wait()
        
        let childChannel: Channel = try! channel!.pipeline.handler(type: NIOSSHHandler.self).flatMap { sshHandler in
            let promise = self.channel!.eventLoop.makePromise(of: Channel.self)
            sshHandler.createChannel(promise) { childChannel, channelType in
                guard channelType == .session else {
                    return self.channel!.eventLoop.makeFailedFuture(SSHClientError.invalidChannelType)
                }
                return childChannel.pipeline.addHandlers([ExecutionHandler(), ErrorHandler()])
            }
            return promise.futureResult
        }.wait()
        self.childChannel = childChannel

The ExecutionHandler that is passed to the childchannel's pipeline overrides func triggerUserOutboundEvent and triggers an internal outbound event that contains a SSHChannelRequestEvent.ExecRequest :

public func triggerUserOutboundEvent(context: ChannelHandlerContext, event: Any, promise: EventLoopPromise<Void>?) {
        guard let (buffer, responseHandler) = event as? (ByteBuffer, ((String, Int32) -> Void)?) else {
            promise?.fail(SSHError.invalidData)
            return
        }
        self.responseHandler = responseHandler
        let _ = context.triggerUserOutboundEvent(SSHChannelRequestEvent.ExecRequest(command: String(buffer: buffer), wantReply: false))
    }

This works fine for one request. But if i try to trigger multiple outbound events, i.e. send multiple SSHChannelRequestEvent.ExecRequest, it fails by returning inputClosed and the child channel is closed. This is problematic since it loses the state of the requests. If I wanted to change the directory with one request, the second one would start again in the root dir.
I am fairly new to SwiftNIO and NIOSSH, so I wanted to ask if this is a known problem or am I just using the framework wrong?
The READ.me states ...A session channel represents an invocation of a command. Does this mean a session is supposed to be closed after one command? If so, is there still a possibility to maintain the state of a request? For now I've been concatenating related commands together with command1 && command2 and executing them in one request. But this seems more like a hack to me.

Any help is greatly appreciated!

ChannelError: operationUnsupported when executing commands from a daemon

Context

NIO SSH Commit Hash

36a4f6f4179ef5ebbeca501fc2125734b9f46290

Swift version

swift-driver version: 1.90.11.1 Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)
Target: arm64-apple-macosx14.0

uname -a

Darwin MacBook-Pro.local 23.2.0 Darwin Kernel Version 23.2.0: Wed Nov 15 21:55:06 PST 2023; root:xnu-10002.61.3~2/RELEASE_ARM64_T6020 arm64

Running Xcode 15.3 with macOS Sonoma 14.2.1 (23C71)

Problem

When using nio-ssh to execute ssh commands in a daemonized context (built executable launched using launchctl with a config in /Library/LaunchDaemons) a ChannelError (operationUnsupported) is thrown.

I'm unsure if this is a problem just with nio-ssh or nio in general. Could it be that certain network operations aren't permitted from within a daemon?

Any information/help on this matter is greatly appreciated!

Forum post: https://forums.developer.apple.com/forums/thread/749910

Reproduction

Reproduction can be found here: https://github.com/eliaSchenker/nio-ssh-daemon-issue/tree/main

To run the reproduction follow these steps:

  • Build using Xcode (Product > Build)
  • Find the executable in the build folder (Product > Show Build Folder in Finder)
  • Move the executable to /Library/PrivilegedHelperTools
  • Create a daemon configuration in /Library/LaunchDaemons/nio-ssh-daemon.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
	<dict>
		<key>Label</key>
		<string>nio-ssh-daemon</string>
		<key>ProgramArguments</key>
                 <array>
                 	<string>/Library/PrivilegedHelperTools/nio-ssh-daemon</string>
	    		<string>username:password@host</string>
			<string>ls -la</string>
                 </array>
		<key>KeepAlive</key>
		<true/>
		<key>ProcessType</key>
		<string>Interactive</string>
		<key>StandardOutPath</key>
		<string>/Library/Logs/nio-ssh-daemon.out.log</string>
		<key>StandardErrorPath</key>
		<string>/Library/Logs/nio-ssh-daemon.err.log</string>
	</dict>
</plist>

making sure to adjust the program arguments to include an host with username and password.

  • Load the daemon using
sudo launchctl load nio-ssh-daemon.plist
  • When opening Console.app, navigating to Log Reports and opening nio-ssh-daemon.out.log the logged error will be shown:
Creating bootstrap
Connecting channel
Creating child channel
Waiting for connection to close
Error in pipeline: operationUnsupported
An error occurred: commandExecFailed

If the executable is run manually without a daemon it will work correctly:

./nio.ssh-daemon username:password@host

The reproduction is a copy of the example in the repository (https://github.com/apple/swift-nio-ssh/tree/main/Sources/NIOSSHClient) with slight modifications to log errors instead of using try!.

Client remote port forwarding?

Hey guys, is it possible to implement remote/reverse port forwarding from the client side? Something similar as -R switch used in the ssh command: ssh -R 9090:localhost:8080 some.remoteserver.com

I'm currently using swift-nio to run a local web server + websocket in the iOS application, and I would like to expose it using the remote ssh server without any additional port forwarding configured on the router. Is it something like this even possible using swift-nio? Thanks!

Add better handling for SSH_MSG_UNIMPLEMENTED

We can receive SSH_MSG_UNIMPLEMENTED whenever we send a message the remote peer doesn't like. In almost all cases this is going to be an unrecoverable error for us, but we should validate we report the error sensibly and clearly.

Support keyboard-interactive authentication

While implementing 2-factor login in my app, I noticed that swift-nio-ssh is missing support for keyboard-interactive authentication. I can create a pull request to add it to the NIOSSHAvailableUserAuthenticationMethods: OptionSet and make the client recognize keyboard-interactive messages. However, I am concerned that this change may cause issues on the server-side. So I created this issue to ask if you have any plans to support keyboard-interactive authentication in the future.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.