Lua Scripting Plugin
Relevant source files
The following files were used as context for generating this wiki page:
The Lua Scripting Plugin for sshpiperd enables dynamic routing and authentication of SSH connections using the Lua programming language. It allows administrators to implement complex routing logic, access control, and credential mapping based on connection metadata such as usernames, remote IP addresses, and unique connection identifiers.
As a gRPC-based plugin, it intercepts downstream connection requests and communicates with sshpiperd to determine the appropriate upstream destination and authentication parameters. It supports various authentication methods, including password, public key, and keyboard-interactive challenges. Sources: plugin/lua/README.md:3-9, plugin/lua/main.go:21-25
Architecture and Design
The plugin is built using the gopher-lua library, which provides a Go-based Lua VM. It operates by loading a user-defined script and mapping specific Go-side lifecycle events to Lua functions.
State Management and Pooling
To handle high-performance requirements and concurrent connections, the plugin implements a sync.Pool of Lua states. This pool minimizes the overhead of initializing a new Lua VM for every request while ensuring that script variables can be shared across callbacks if necessary.
The diagram shows how sshpiperd requests are processed by the Lua plugin using a pooled VM instance to execute user-defined scripts. Sources: plugin/lua/lua.go:25-33, plugin/lua/lua.go:106-121
Hot Reloading
The plugin supports hot-reloading of the Lua script without interrupting the main sshpiperd daemon. Upon receiving a SIGHUP signal, the plugin drains the existing state pool, validates the new script, and repopulates the pool with fresh states. Sources: plugin/lua/main.go:42-61, plugin/lua/README.md:33-46
Lua Script API
The plugin exposes connection metadata to Lua via a conn table and expects return values in the form of an "Upstream Table."
Connection Metadata Table
The conn table provided to Lua functions contains the following fields:
| Field | Description | Source |
|---|---|---|
sshpiper_user | The username provided by the downstream client. | plugin/lua/lua.go:230 |
sshpiper_remote_addr | The IP address of the client connection. | plugin/lua/lua.go:231 |
sshpiper_unique_id | A unique UUID for the specific connection instance. | plugin/lua/lua.go:232 |
Supported Callbacks
Administrators can define various callbacks to handle different stages of the SSH connection:
sshpiper_on_password(conn, password): Triggered during password authentication.sshpiper_on_publickey(conn, key): Triggered during public key authentication.sshpiper_on_keyboard_interactive(conn, challenge): Allows for multi-step challenges (e.g., 2FA).sshpiper_on_new_connection(conn): Pre-authentication check to allow or block IPs.sshpiper_on_verify_hostkey(conn, hostname, netaddr, key): Validates upstream host keys.
Sources: plugin/lua/lua.go:56-67, plugin/lua/README.md:58-115
Upstream Configuration Table
When a callback successfully identifies a target, it must return a table with the following structure:
return {
host = "upstream.server:22", -- Required: host:port
username = "remote_user", -- Optional: defaults to downstream user
ignore_hostkey = true, -- Optional: skip verification (not for production)
password = "secret_password", -- Optional: override password for upstream
private_key_data = "..." -- Optional: PEM-encoded private key for upstream
}
Sources: plugin/lua/lua.go:377-440, plugin/lua/README.md:133-146
Data Flow: Password Authentication
The following sequence illustrates the data flow when a user authenticates with a password.
This diagram depicts the synchronous communication between the client, proxy, and the Lua-backed plugin during password authentication. Sources: plugin/lua/lua.go:318-350, libplugin/plugin.proto:115-118
Logic Implementation Details
Upstream Parsing
The plugin logic in parseUpstreamTable ensures that the host field is present and correctly formatted. It defaults the upstream username to the downstream conn.sshpiper_user if not explicitly provided in the Lua table. It also handles the conversion of PEM-encoded strings into appropriate authentication types for the sshpiperd core. Sources: plugin/lua/lua.go:387-438
Logging Integration
The plugin injects a global function sshpiper_log(level, message) into the Lua environment. This allows script authors to send logs back to the Go-side logrus logger used by sshpiperd. Supported levels include debug, info, warn, and error. Sources: plugin/lua/lua.go:146-167, plugin/lua/README.md:120-131
Custom Search Paths
Users can extend the Lua environment by specifying a --lua-path. The plugin modifies the Lua package.path to include these directories, as well as the directory containing the main script, facilitating modular script development. Sources: plugin/lua/lua.go:169-211, plugin/lua/main.go:32-37
Summary
The Lua Scripting Plugin provides a flexible extension point for sshpiperd, moving routing logic out of static configurations and into a programmable environment. By leveraging state pooling and gRPC, it maintains high performance while offering granular control over connection metadata, upstream remapping, and authentication challenges. Sources: plugin/lua/README.md:3-15, plugin/lua/lua.go:48-83