SwiftNIO SSH

repository·main·Indexed 19 days ago

https://github.com/apple/swift-nio-ssh

A programmatic implementation of the SSHv2 protocol built on SwiftNIO. It provides building blocks for developers to create custom SSH clients and servers, supporting session channels, direct and reverse TCP port forwarding, and password or public key authentication. It utilizes modern cryptographic primitives including Ed25519, ECDSA, AES-GCM, and x25519.

Tokens
3.8K
Snippets
3
Records
17
Agent score
18%

What's inside SwiftNIO SSH

  1. What is SwiftNIO SSH?

    main
    SwiftNIO SSH is a programmatic implementation of the SSH protocol built on top of SwiftNIO. Unlike standard SSH clients or servers (like OpenSSH), it is designed as a collection of APIs that provide the building blocks for developers to implement their own SSH-speaking endpoints. It functions similarly to libssh2, providing the networking and cryptographic layers necessary to drive SSH sessions directly from within Swift services.
  2. Overview of SwiftNIO SSH topics

    main

    SwiftNIO SSH provides a set of abstractions for managing SSH connections, port forwarding, user authentication, host key verification, and child channels. The library is organized into several functional areas:

    • Connections: Managing the lifecycle of an SSH connection using NIOSSHHandler and configuring clients or servers via SSHClientConfiguration and SSHServerConfiguration.
    • Port Forwarding: Handling global requests and port forwarding through GlobalRequest and GlobalRequestDelegate.
    • User Authentication: Implementing authentication logic for clients and servers using delegates like NIOSSHClientUserAuthenticationDelegate and NIOSSHServerUserAuthenticationDelegate.
    • Host Key Verification: Verifying server identities using NIOSSHClientServerAuthenticationDelegate.
    • Child Channels: Managing sub-channels (like shell or direct-tcp) using SSHChannelType, SSHChildChannelOptions, and handling channel-specific events.
    • Keys and Signatures: Working with cryptographic material including NIOSSHPublicKey, NIOSSHPrivateKey, and NIOSSHSignature.
    • Errors: Handling SSH-specific failures via NIOSSHError.
  3. What is SwiftNIO SSH and how is it used?

    main

    SwiftNIO SSH is a programmatic implementation of the SSHv2 protocol, designed as a collection of building blocks rather than a ready-to-use client or server. It is more akin to libssh2 than openssh, allowing developers to drive SSH sessions directly from within Swift services.

    Key Features:

    • Supports session, directTCPIP, and forwardedTCPIP channel types.
    • Supports Password and Public Key user authentication.
    • Uses modern cryptographic primitives: Ed25519, ECDSA (P256, P384, P521), AES-GCM, and x25519.
    • Supports all platforms compatible with SwiftNIO and Swift Crypto.

    Minimum Swift Versions:

    • 0.9.0 and newer: Swift 5.8+
  4. Remote Port Forwarding and Global Requests

    main

    Remote port forwarding allows a client to request that the server listen on a specific address/port and forward inbound connections back to the client.

    Initiating Requests (Client)

    Clients use NIOSSHHandler.sendGlobalRequest to send global requests. Supported requests are:

    • GlobalRequest.TCPForwardingRequest.listen(host:port:): Tells the server to start listening.
    • GlobalRequest.TCPForwardingRequest.cancel(host:port:): Tells the server to stop listening.

    Handling Requests (Server)

    Servers implement GlobalRequestDelegate to receive and respond to these requests. The method tcpForwardingRequest(_:handler:promise:) is invoked when a request is received. Once the server handles the request, the result is passed into the promise.

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

  5. Handle SSH channel data with SSHChannelData

    main

    All data transmitted over an SSH channel is encapsulated in the SSHChannelData structure. This structure handles both regular and extended data types via SSHChannelData.DataType:

    • .channel: Used for the vast majority of core data. In session channels, this represents standard input/output. In TCP forwarding channels, this represents the forwarded data.
    • .stdErr: Used in session channels to represent standard error.
  6. How SSH channels and the NIOSSHHandler work together

    main

    SwiftNIO SSH uses a multiplexed architecture. The core protocol logic is handled by a NIOSSHHandler (a SwiftNIO ChannelHandler) within a primary NIO Channel.

    Because SSH is multiplexed, the connection is subdivided into multiple bidirectional communication channels. SwiftNIO SSH represents each SSH channel as a new, independent NIO Channel (a "child channel").

    Channel Types:

    • session: Used for invoking programs or shells (the most common type).
    • directTCPIP: Used for direct TCP port forwarding (client to server).
    • forwardedTCPIP: Used for remote TCP port forwarding (server to client).

    Data Handling: Channels communicate using SSHChannelData.

    • In session channels: SSHChannelData/DataType/channel is used for stdin and stdout, while SSHChannelData/DataType/stdErr is used for stderr.
    • In TCP forwarding channels: SSHChannelData/DataType/channel is used for all forwarded data.
  7. Manage Remote Port Forwarding and Global Requests

    main

    Remote port forwarding (client asking the server to listen on a port) is handled via Global Requests.

    Client-side: Initiating Requests

    Clients initiate these requests using NIOSSHHandler/sendTCPForwardingRequest(_:promise:). Supported requests include:

    • GlobalRequest/TCPForwardingRequest/listen(host:port:): Tells the server to listen on a specific host/port.
    • GlobalRequest/TCPForwardingRequest/cancel(host:port:): Tells the server to stop listening.

    Server-side: Handling Requests

    Servers must implement a GlobalRequestDelegate to respond to these requests. The primary method is: tcpForwardingRequest(_:handler:promise:).

    When a request is accepted, the server sends the resulting forwarded channels to the client using the SSHChannelType/forwardedTCPIP(_:) channel type.

  8. Supported SSHv2 features in SwiftNIO SSH

    main

    SwiftNIO SSH supports the SSHv2 protocol with the following feature set:

    • Session Channels: All session channel features, including shell and exec channel requests.
    • Port Forwarding: Both direct and reverse TCP port forwarding.
    • User Authentication: Support for both password and public key authentication.
    • Cryptography: Uses modern cryptographic primitives only:
      • Asymmetric: Ed25519 and ECDSA over major NIST curves (P256, P384, P521).
      • Symmetric: AES-GCM.
      • Key Exchange: x25519.
    • Platforms: Supports all platforms supported by SwiftNIO and Swift Crypto.
  9. Direct Port Forwarding (Client to Server)

    main
    Direct port forwarding allows a client to forward inbound connections to a server, which then forwards them to a specific host and port. Clients initiate this by opening channels with the .directTCPIP channel type.
  10. How SwiftNIO SSH uses child channels

    main

    SwiftNIO SSH implements the SSH protocol via the NIOSSHHandler, which is a SwiftNIO ChannelHandler. Because SSH is a multiplexed protocol, SwiftNIO SSH uses a "child channel" abstraction.

    When a peer creates a new SSH channel, the library creates a new NIO Channel to represent that specific channel's traffic. This allows you to interact with different SSH streams (like a shell session or a port forward) using standard NIO Channel patterns. Events within a single child channel are strictly ordered, but events across different channels may be interleaved.

    Supported channel types include:

    • session: Used for invoking programs or shells (the most common type).
    • directTCPIP: Used for client-to-server TCP port forwarding.
    • forwardedTCPIP: Used for server-to-client TCP port forwarding.
    ┌ ─ NIO Channel ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐
    │     ┌────────────────────────────────────────────────┐    │
    │     │                 NIOSSHHandler                │    │
    │     └────────────────────────────────────────────────┘    │
    │                                                           │
    │     ┌────────────────────────────────────────────────┐    │
    │     │               SSH Child Channel                │    │
    │     │   ┌────────────────────────────────────────┐   │    │
    │     │   │              User Handler              │   │    │
    │     │   └────────────────────────────────────────┘   │    │
    │     └────────────────────────────────────────────────┘    │
    └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘
  11. Enable half-closure for SSH child channels

    main

    The SSH protocol relies heavily on half-closure in child channels. By default, NIO Channels have half-closure disabled, which causes unexpected behavior in SwiftNIO SSH child channels.

    Requirement: You must explicitly enable half-closure support for all child channels:

    channel.setOption(ChannelOptions.allowRemoteHalfClosure, true)

    Behavior with half-closure enabled:

    • Receiving EOF: The remote peer sending EOF will trigger the inbound user event ChannelEvent.inputClosed.
    • Sending EOF: To send an EOF from your side, call close(mode: .output).