Auth Methods & Callbacks
Relevant source files
The following files were used as context for generating this wiki page:
Introduction
Authentication in sshpiper is managed through a plugin-based architecture where the daemon (sshpiperd) delegates authentication decisions and upstream routing to external plugins. The core of this system relies on a set of defined authentication methods and callback functions that allow plugins to verify downstream clients and determine the appropriate upstream connection parameters.
The system supports standard SSH authentication methods including NONE, PASSWORD, PUBLICKEY, and KEYBOARD_INTERACTIVE. Plugins implement specific handlers for these methods to perform tasks such as verifying passwords, checking authorized keys, or presenting multi-factor authentication (MFA) challenges. This modular approach allows for complex routing logic and additional security challenges to be integrated seamlessly into the SSH proxy flow.
Sources: libplugin/plugin.proto:89-94, README.md:20-30, libplugin/pluginbase.go:42-65
Core Auth Methods
The project defines four primary authentication methods that a plugin can advertise to a client or handle during a session. These are defined within the gRPC protocol as the AuthMethod enum.
| Method | Value | Description |
|---|---|---|
NONE | 0 | Attempt connection without explicit credentials. |
PASSWORD | 1 | Standard password-based authentication. |
PUBLICKEY | 2 | RSA, Ed25519, or other public key-based authentication. |
KEYBOARD_INTERACTIVE | 3 | Multi-step prompt/response authentication (used for TOTP, MFA). |
Sources: libplugin/plugin.proto:89-94, libplugin/plugin.pb.go:34-45
The Authentication Flow
The authentication flow starts when a downstream client connects. sshpiperd queries the plugin for supported methods and then invokes the corresponding callback based on the client's choice.
Callback Architecture
Plugins interact with sshpiperd through a suite of gRPC callbacks. These callbacks are categorized into authentication handlers and lifecycle notifications.
Authentication Callbacks
These callbacks are responsible for verifying credentials and returning an Upstream object which contains the destination address and the mapped authentication to use for the upstream server.
NoneAuth: Invoked when the client sends a 'none' auth request.PasswordAuth: Receives the downstream password and returns upstream routing.PublicKeyAuth: Receives the public key blob. For certificates, it may also include CA fingerprints in metadata.KeyboardInteractiveAuth: A bi-directional stream for complex prompt-response sequences.
Sources: libplugin/pluginbase.go:46-57, cmd/sshpiperd/internal/plugin/grpc.go:434-474
Notification & Lifecycle Callbacks
These allow plugins to react to connection events without necessarily controlling the routing logic.
NewConnection: Called when a downstream connection is first established.UpstreamAuthFailure: Notifies the plugin when the upstream server rejects the mapped credentials.Banner: Requests a custom string to display to the client before authentication.PipeStart/PipeError: Notifies the plugin of the success or failure of the data pipe.
Sources: libplugin/pluginbase.go:44, libplugin/pluginbase.go:59-65, cmd/sshpiperd/internal/plugin/grpc.go:102-120
Keyboard Interactive Auth (MFA)
The KeyboardInteractiveAuth is implemented as a gRPC stream to support multiple rounds of prompts. This is commonly used for 2FA/TOTP verification.
In the workingdir plugin, this is used to implement TOTP:
- The plugin checks if a
totpfile exists in the user's directory. - If the user hasn't been verified, it forces
keyboard-interactive. - It prompts for the "Authentication code:".
- Upon successful validation, it returns an
Upstreamwithretry_current_pluginand metadata marking the user as "checked".
Sources: plugin/workingdir/main.go:81-125, libplugin/plugin.proto:133-162
Plugin Chaining
sshpiperd supports chaining multiple plugins. When plugins are chained, the authentication responsibility passes from one to the next until an upstream connection is resolved or the chain is broken.
Chain Logic
- The
ChainPluginsstructure manages an ordered list of plugins. - If a plugin returns
UpstreamNextPluginAuth,sshpiperdincrements the index and calls the next plugin in the chain. - Metadata can be passed between plugins via the
UpstreamNextPluginAuth.Metamap.
Data Structures
ConnMeta
Passed to almost every callback, this structure provides the context of the downstream connection.
| Field | Type | Description |
|---|---|---|
user_name | string | The username provided by the downstream client. |
from_addr | string | The remote IP and port of the client. |
uniq_id | string | A unique UUID generated for the connection. |
metadata | map<string, string> | Extensible key-value pairs (e.g., CA fingerprints). |
Sources: libplugin/plugin.proto:7-12, libplugin/plugin.pb.go:73-82
Upstream
The result of a successful auth callback, defining how to connect to the target.
| Field | Type | Description |
|---|---|---|
uri | string | Target destination (e.g., tcp://host:port). |
user_name | string | The username to use for upstream authentication. |
auth | oneof | Can be none, password, private_key, remote_signer, next_plugin, or retry_current_plugin. |
Sources: libplugin/plugin.proto:14-34, libplugin/plugin.pb.go:129-155
Conclusion
The Authentication and Callback system provides a flexible framework for extending SSH capabilities. By separating authentication logic into gRPC-based plugins, sshpiper enables developers to implement custom security policies, integrate with external identity providers via Lua or YAML, and support complex multi-factor authentication flows while maintaining a standard SSH experience for the end user.
Sources: CONTRIBUTING.md:65-71, README.md:143-155