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} 32
Enum for the types of SSH requests a client can send to a server:
RequestType_Auth
RequestType_ChannelOpen
RequestType_Channel
RequestType_Service
RequestType_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 Bind
ptr::Union{Nothing, Ptr{LibSSH.lib.ssh_bind_struct}}
addr::String
port::UInt64
hostkey::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 ServerCallbacks
Wrapper 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_function
gssapi_accept_sec_ctx_function
gssapi_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)::AuthStatus
on_auth_none
:f(::Session, ::String, userdata)::AuthStatus
on_auth_gssapi_mic
:f(::Session, ::String, ::String, userdata)::AuthStatus
on_auth_pubkey
:f(::Session, ::String, ::SshKey, ::Char, userdata)::AuthStatus
on_service_request
:f(::Session, ::String, userdata)::Bool
on_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)::Bool
The 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 SessionEvent
ptr::Union{Nothing, Ptr{LibSSH.lib.ssh_event_struct}}
session::LibSSH.Session
lock::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 expectfoo
andbar
as 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 DemoServer
bind::LibSSH.Bind
listener_task::Union{Nothing, Task}
sshchan::Union{Nothing, LibSSH.SshChannel}
verbose::Bool
password::Union{Nothing, String}
allow_auth_none::Bool
clients::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 anInterruptException
will 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_timeout
no 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 istrue
it 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
.