Server support
The symbols documented on this page are intended to be safe. They may throw exceptions but they should never cause memory corruptions or segfaults if used correctly.
LibSSH.RequestType — Typeprimitive type RequestType <: Enum{Int32} 32Enum for the types of SSH requests a client can send to a server:
RequestType_AuthRequestType_ChannelOpenRequestType_ChannelRequestType_ServiceRequestType_Global
SSH Binds
The main type you care about for writing a server is the Bind, which is somewhat analogous to a listening socket. It can be bound to a port and when a connection is attempted by a client it can be accepted with lib.ssh_bind_accept(). Implementing everything else (including authentication) must be done yourself.
If you're writing a server and want to implement keyboard-interactive authentication, also see message_auth_interactive_request.
There are some examples of using libssh's C API to write a server here (using callbacks), and another one demonstrating keyboard-interactive authentication here (not using callbacks). You can also check the source for the Demo server.
LibSSH.Bind — Typemutable struct Bindptr::Union{Nothing, Ptr{LibSSH.lib.ssh_bind_struct}}addr::Stringport::UInt64hostkey::Union{Nothing, String}key::Union{Nothing, LibSSH.PKI.SshKey}auth_methods::Vector{LibSSH.AuthMethod}log_verbosity::Int64_listener_event::Base.Event_listener_started::Bool_lock::ReentrantLock_message_callback::Union{Nothing, Function}_message_callback_userdata::Any
Wrapper around lib.ssh_bind.
LibSSH.listen — Functionlisten(handler::Function, bind::LibSSH.Bind; poll_timeout)
High-level function to listen for incoming requests and pass them off to a handler function. This will already set the auth methods on the session (from Bind.auth_methods) before calling the handler.
The poll_timeout argument refers to the timeout for polling the bind socket for new connections. It must be >0 because otherwise it would never wake up if the socket was closed while waiting, but other than that the exact value doesn't matter much. It'll only control how frequently the listen loop wakes up to check if the bind has been closed yet.
LibSSH.wait_for_listener — Functionwait_for_listener(bind::LibSSH.Bind)
Waits for the main loop of listen to begin running on the bind.
LibSSH.handle_key_exchange — Functionhandle_key_exchange(session::LibSSH.Session) -> Bool
Non-blocking wrapper around lib.ssh_handle_key_exchange(). Returns true or false depending on whether the exchange succeeded.
LibSSH.set_auth_methods — Methodset_auth_methods(
session::LibSSH.Session,
auth_methods::Vector{LibSSH.AuthMethod}
)
Set authentication methods on a Session.
Wrapper around lib.ssh_set_auth_methods().
LibSSH.set_auth_methods — Methodset_auth_methods(
msg::Ptr{LibSSH.lib.ssh_message_struct},
auth_methods::Vector{LibSSH.AuthMethod}
) -> Int32
Set authentication methods for a lib.ssh_message.
Wrapper around lib.ssh_message_auth_set_methods().
LibSSH.Callbacks.ServerCallbacks — Typemutable struct ServerCallbacksWrapper around lib.ssh_server_callbacks_struct.
LibSSH.Callbacks.ServerCallbacks — MethodServerCallbacks(; ...) -> LibSSH.Callbacks.ServerCallbacks
ServerCallbacks(
userdata;
on_auth_password,
on_auth_none,
on_auth_gssapi_mic,
on_auth_pubkey,
on_service_request,
on_channel_open_request_session
) -> LibSSH.Callbacks.ServerCallbacks
Create a callbacks object to set on a server. This has basically the same behaviour as ChannelCallbacks(), except that there are some callbacks that are unsupported due to lack of documentation:
gssapi_select_oid_functiongssapi_accept_sec_ctx_functiongssapi_verify_mic_function
And lib.ssh_key arguments will be converted to a non-owning ssh.PKI.SshKey.
Do not use ssh.Session or ssh.PKI.SshKey arguments outside the callback functions. They are temporary non-owning wrappers, and they will be unusable after the callback has been executed.
Arguments
All of these are also properties that can be set after creation.
userdata: An arbitrary object that will be passed to each callback.on_auth_password:f(::Session, ::String, ::String, userdata)::AuthStatuson_auth_none:f(::Session, ::String, userdata)::AuthStatuson_auth_gssapi_mic:f(::Session, ::String, ::String, userdata)::AuthStatuson_auth_pubkey:f(::Session, ::String, ::SshKey, ::Char, userdata)::AuthStatuson_service_request:f(::Session, ::String, userdata)::Boolon_channel_open_request_session:f(::Session, userdata)::Union{SshChannel, Nothing}
LibSSH.set_server_callbacks — Functionset_server_callbacks(
session::LibSSH.Session,
callbacks::LibSSH.Callbacks.ServerCallbacks
) -> LibSSH.Callbacks.ServerCallbacks
Set callbacks for a Session. Wrapper around lib.ssh_set_server_callbacks().
LibSSH.set_message_callback — Functionset_message_callback(
f::Function,
bind::LibSSH.Bind,
userdata
)
Set message callbacks for the sessions accepted by a Bind. This must be set before listen is called to take effect. listen will automatically set the callback before passing the session to the user handler.
The callback function must have the signature:
f(session::Session, msg::lib.ssh_message, userdata)::BoolThe return value indicates whether further handling of the message is necessary. It should be true if the message wasn't handled or needs to be handled by libssh, or false if the message was completely handled and doesn't need any more action from libssh.
LibSSH.get_error — Methodget_error(bind::LibSSH.Bind) -> String
Get the last error set by libssh. Wrapper around lib.ssh_get_error().
Base.close — Methodclose(bind::LibSSH.Bind)
Close and free the bind.
Base.lock — Methodlock(bind::LibSSH.Bind)
Lock a bind for thread-safe operations.
Base.unlock — Methodunlock(bind::LibSSH.Bind)
Unlock a bind.
Base.isopen — Methodisopen(bind::LibSSH.Bind) -> Bool
Check if the bind has been free'd yet.
Each Session accepted by a Bind must be polled for all the handlers to execute. This is possible through the SessionEvent type.
LibSSH.SessionEvent — Typemutable struct SessionEventptr::Union{Nothing, Ptr{LibSSH.lib.ssh_event_struct}}session::LibSSH.Sessionlock::ReentrantLock
This object wraps a lib.ssh_event, but it's designed to only allow adding a single session to it. Use this in a server to poll the session. It is threadsafe.
LibSSH.SessionEvent — MethodSessionEvent(session::LibSSH.Session) -> LibSSH.SessionEvent
Create an empty SessionEvent.
LibSSH.event_dopoll — Functionevent_dopoll(event::LibSSH.SessionEvent) -> Any
Non-blocking wrapper around lib.ssh_event_dopoll(). This may trigger callbacks on the session and its channels.
Returns either SSH_OK or SSH_ERROR.
Base.isassigned — Methodisassigned(event::LibSSH.SessionEvent) -> Bool
Check if a SessionEvent holds a valid pointer to a lib.ssh_event.
Base.close — Methodclose(event::LibSSH.SessionEvent; unsafe)
Removes the Session from the underlying ssh_event and frees the event memory. This function may be safely called multiple times, and the event will be unusable afterwards.
Demo server
The DemoServer is an extremely simple and limited implementation of an SSH server using the libssh server API. It's sole reason for existence is to be used in test suites to test client code. Do not expose this publicly! See the constructors docstrings for examples of how to use it (the LibSSH.jl test suite may also be informative).
Supported features:
- Password authentication: only the password is checked, not the username.
- Keyboard-interactive authentication: the server will give two prompts for a
Password:andToken:and expectfooandbaras answers, respectively. - Command execution: note that requested environment variables from the client are currently ignored, and the command output will only be sent back to the client after the command has finished.
- Direct port forwarding
Unsupported features (that may be implemented in the future):
- Public key authentication
- GSSAPI authentication
- Reverse port forwarding
One might ask the question, why use a demo server for testing instead of something battle-hardened like sshd? Well, turns out that it's impossible to run sshd as a non-root user unless you disable password authentication (because sshd needs to read /etc/passwd), which is definitely something we want to test. Plus, having a custom server makes it simpler to set up in just the way we want.
LibSSH.Demo.DemoServer — Typemutable struct DemoServerbind::LibSSH.Bindlistener_task::Union{Nothing, Task}sshchan::Union{Nothing, LibSSH.SshChannel}verbose::Boolpassword::Union{Nothing, String}allow_auth_none::Boolclients::Vector{LibSSH.Demo.Client}
LibSSH.Demo.DemoServer — MethodDemoServer(
f::Function,
args...;
timeout,
kill_timeout,
kwargs...
) -> Tuple{Any, Any}
Do-constructor to execute a function f() while the server is running and have it safely cleaned up afterwards. There are two keyword arguments to be aware of:
timeout(default 10s): set a timeout in seconds forf(). Iff()doesn't finish before the timeout anInterruptExceptionwill be thrown to it.kill_timeout(default 3s): set a waiting time in seconds forf()to exit after throwing it anInterruptException. Sometimes you may want to cleanup things before exiting, and this gives some time to do that. Iff()isn't finished afterkill_timeoutno futher action will be taken.
args and kwargs will all be passed to DemoServer(::Int).
Examples
julia> import LibSSH.Demo: DemoServer
julia> DemoServer(2222; password="foo") do
run(`sshpass -p foo ssh -o NoHostAuthenticationForLocalhost=yes -p 2222 localhost echo 'Hello world!'`)
end
Hello world!LibSSH.Demo.DemoServer — MethodDemoServer(
port::Int64;
verbose,
password,
allow_auth_none,
auth_methods,
log_verbosity
) -> LibSSH.Demo.DemoServer
Creates a DemoServer.
Arguments
port: The port to listen to.verbose=false: This verbosity doesn't refer to the log messages from libssh but from theDemoServer. If this istrueit print messages on events like authentication etc. Useful for high-level debugging. The events can always be printed afterwards withDemo.print_timeline.password=nothing: The password to use if password authentication is enabled.allow_auth_none: Whether to allow authentication without any credentials being presented.auth_methods=[AuthMethod_None, AuthMethod_Password]: A list of authentication methods to enable. Seessh.AuthMethod.log_verbosity=nothing: Controls the logging of libssh itself. This could be e.g.lib.SSH_LOG_WARNING(see the upstream documentation).
LibSSH.Demo.print_timeline — Methodprint_timeline(ds::LibSSH.Demo.DemoServer)
Print a nicely formatted timeline of callbacks and their logged data. Useful when debugging.
LibSSH.Demo.start — Methodstart(demo_server::LibSSH.Demo.DemoServer)
Start a DemoServer, which means bind to a port and start the ssh.listen loop.
LibSSH.Demo.stop — Methodstop(demo_server::LibSSH.Demo.DemoServer)
Stop a DemoServer.