Reverse SSH support
General
Adds capability to OpenSync nodes to accept SSH connections and provide remote Shell access through pre-defined configuration and for a pre-defined duration of time.
By default, access through Secure Shell (SSH) to an OpenSync node is not supported for security reasons. The “RevSSH” feature adds capability to OpenSync nodes to accept SSH connections and provide remote Shell access through pre-defined configuration and for a pre-defined duration of time.
Shell access to an OpenSync node can be established through a “Jump Server” (What is a Jump Server?) setup and run time configurations (IP address, Port, credentials, certificates etc.) through Northbound API.
Theory of operation
After a successful configuration through a controller an OpenSync node would:
Instantiate a temporary SSH server instance on the node with temporary credentials allowing local connections only
Set up an outbound connection to the Jump Server with reverse SSH tunnel / port forwarding enabled
When a user logs in to the Jump Server and connects to pre-configured port, the connection is forwarded over the secure channel back to OpenSync node -- establishing a connection to the temporary SSH server instance that was instantiated on the node
This reverse SSH tunnel will be established for a configurable max time duration only to abide best security practices.
The OpenSync node will issue audit logs and we encourage you to have them also on RevSSH jump server.
OpenSync node can be of gateway or extender type and user can initiate multiple sessions at the same time on a single node
At a minimum, the following parameters will need to be configured by the user (corresponding to RevSSH OVSDB schema): server_host, server_port, server_user, server_pub_key, tunnel_remote_bind_port, tunnel_local_port. Other parameters may be left at default or they might need to be set.
If OpenSync node fails to SSH to the jump RevSSH server due to authentication failure, it retries the connection every 5 seconds, for max 3 minutes.
Status will be reported through
RevSSH->tunnel_status
A RevSSH session is automatically timed-out and is destroyed after the configured maximum time regardless of activity (default 8h) and/or after a configured idle/inactivity timeout (default 10 minutes)
Northbound API
OVSDB schema
Name | Type | Description |
---|---|---|
server_host | string | RevSSH server address: IPv4, IPv6 or FQDN. |
server_port | integer [optional] | RevSSH server port. Default: 22 |
server_user | string | Specifies the user for the node to log in as on the jump RevSSH server. |
server_pubkey | ARRAY of string [4] | 1 or more (max 4) public keys of a RevSSH server user(s). Note: The design allows only up to 1 RevSSH tunnel session at any given time (max 1 RevSSH row allowed). However, the same RevSSH tunnel can be reused:
The public keys configured here will be temporarily (just for the duration of a RevSSH session) added to node’s authorized_keys list. In the SSH public key format - RFC 4253. |
tunnel_remote_bind_addr | string [optional] | Remote bind address. Optional, By default, if not set (recommended), TCP listening sockets on the server will be bound to the loopback interface only. Default: 127.0.0.1 |
tunnel_remote_bind_port | integer | Remote bind port TCP listening socket on the server bind port. Care must be taken to not use conflicting ports, i.e. to not use a port number that may already be in use by another service (or another RevSSH session) on the RevSSH service. It is advised to not used well-known ports (0-1023) – those can be bound to only by a privilleged user anyway. |
tunnel_local_addr | string [optional] | Local bind address. And address to forward the TCP connection to. IPv4 or IPv6 address. Temporary SSH server instance will be started bound to this address. TCP connections will be forwarded to this local address on the node. Default: 127.0.0.1 |
tunnel_local_port | integer [optional] | Local bind port. And local port to forward the TCP connection to. Temporary SSH server instance will be started bound to this port. TCP connections will be forwarded to this local port on the node. Care must be taken to not use a conflicting port that may already be open on the node. It is advised to avoid well-known ports (0-1023). Default: 30022 |
idle_timeout | integer [optional] | Idle timeout per user SSH session. [minutes] If a user SSH session is idle (inactive) for this amount of time, it is automatically closed. Note: More then 1 concurrent SSH sessions per 1 RevSSH tunnel are possible – from the same or from different users (providing that If there are no more active user SSH sessions, the whole RevSSH session is destroyed and resources cleaned up. OpenSync default, if not set: 10 minutes |
session_max_time | integer [optional] | Maximum time allowed for the whole RevSSH session (all user SSH sessions) regardless if the session is active or inactive. In minutes. If this time is reached, regardless if user session is active or not, the whole RevSSH session is destroyed and resources cleaned up. Note: Also, session is destroyed, when the row is deleted. OpenSync default, if not set: 8h (480 minutes) |
node_gen_key_type | enum [optional] |
Optional: Specifies the type of (temporary node) key to create. Default, if not set: Do not create temporary node key. Use node’s static keypair, whatever its type is (usually rsa). Note: Depending on the SSH keytool (or how it was built) on the node not all keytypes may be supported. The temporary node key generation is optional but encouraged as it is arguable more secure (even if public key is left in RevSSH’s server authorized_keys list, it’s not a problem, since it’s valid only temporary for the duration of the RevSSH session). |
node_gen_key_bits | integer [optional] | Specifies the number of bits in the (temporary node) key to create. Must be set if Allowed values:
Default: Do not create temporary node key. Use node’s static keypair, whatever its type and bit length is. |
node_pubkey | string [State, reported by the node – not to be set] | Public key of the node reported here [i.e. this field is NOT a CONFIG item, but State/Status] This will be either node’s “static” public key, or if RevSSH user should then add this public key to RevSSH server user In the SSH public key format - RFC 4253. |
tunnel_status | enum [State, reported by the node – not to be set] |
|
log_last_err_msg | string | Not used |
Notes on “preauth_failure”
OpenSSH deprecated RSA signatures using the SHA-1 hash algorithm in version 8.8 and such a SSH server would, by default, refuse authentications from (old) clients supporting only ssh-rsa (RSA with SHA-1).
The solution is either to make the client use
rsa-sha2-256
(RSA with SHA-256) or the server to
allowssh-rsa
. Note: Since RSA keys are not dependent on the choice of hash function the new public key algorithms reuse the “ssh-rsa” public key format. All aspects of the public key format are kept, including the encoded string “ssh-rsa
". This allows existing RSA keys to be used with the new public key algorithms, without requiring re-encoding or affecting already trusted key fingerprints.In the RevSSH server logs you would typically see messages such as:
sshd[432238]: Unable to negotiate with 80.222.23.16 port 60738: no matching host key type found. Their offer: ssh-rsa [preauth]
The solution for
"preauth_failure"
in the case where the node’s SSH client is offering onlyssh-rsa
(RSA with SHA-1) but the RevSSH server is rejecting it could be: On the RevSSH server side, add the following to/etc/ssh/ssh_config
:HostKeyAlgorithms +ssh-rsa
PubkeyAcceptedAlgorithms +ssh-rsa
For the reverse part of the tunnel: SSH-ing back through the established tunnel, you may also get errors such as “
Unable to negotiate with 127.0.0.1 port 30337: no matching host key type found. Their offer: ssh-rsa
" in which case you would need to add-o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedAlgorithms=+ssh-rsa
to the SSH client commandTo summarize: The solutions are conceptually 2: either server/client mitigations to allow ssh-rsa as mentioned above, or – better, also from security perspective – to upgrade the old SSH clients/servers. Usually the problem is an old Dropbear version on the OpenSync device, so the best option is to upgrade Dropbear on such devices.
Southbound API
OpenSync supports Dropbear, however one can use different SSH server/client libraries of their choice
revssh_t *revssh_new(void);
bool revssh_server_params_set(revssh_t *self, const char *host, int port, const char *user);
bool revssh_authorized_keys_add(revssh_t *self, const char *pubkey);
bool revssh_tunnel_params_set(
revssh_t *self,
osn_ipany_addr_t *remote_bind_addr,
int remote_bind_port,
osn_ipany_addr_t *local_addr,
int local_port);
bool revssh_timeout_set(revssh_t *self, int session_max_time, int idle_timeout);
bool revssh_notify_status_callback_set(revssh_t *self, revssh_status_fn_t *status_fn_cb);
bool revssh_tmpkeygen_set(revssh_t *self, enum revssh_keytype type, int bits);
bool revssh_start(revssh_t *self);
bool revssh_node_pubkey_get(revssh_t *self, char *pubkey, size_t len);
bool revssh_del(revssh_t *self);
void revssh_cleanup_dangling_sessions(void);
Requirements
SSH Server, Client and tools (key generation and copy tools etc.) packages (e.g. dropbear)
Limitations
Dropbear does not support remote TCP forward parameters addresses to be IPv6 addresses (specified in square brackets): So the following
ssh foo@$REVSSH_SERVER -R[::1]:30337:[::1]:40337 -p 2222
does not work. Hence, the OpenSync dropbear backend implementation does not support TCP forward parameters with IPv6 addressesDropbear versions such as v2017.75 or v2019.78 support ssh-rsa signatures and not rsa-sha2-256. Support for these signature algorithms was added in Dropbear version 2020.79
Example configuration and usage
Example with a minimal number of parameters set: Use RevSSH server, specify user, port and user's public key and tunnel_remote_bind_port: (all other parameters will use default values)
ovsh i RevSSH \
server_host:=revssh-opensync.io \
server_user:=opensync_user \
server_port:=2244 \
server_pubkey:="ssh-rsa BBAAB3NzaC1yc2EAAAADAQABAAABAQDYcuU1XfHNO181R6ohzeKIOGjctBgEVkFXWKWQ/1TL8cNYvmmT+pm9oNId5Kl3/YSgD2XCkObg8rTkdlCwKbabAIdyMc0ZEfwu/cwht3g+cxH7ISAVHVbhcNo6eBogRtp7TFiCE2U15m0KAPsALvj56BkwnTIsfEBSa5kICZwOF5V4ErDsyX2YpG2u3s4S4v/gJ+fDgeWbTz1F4b76n/1MTtDGMgUhti96QhuzdmfndZaOzntCsnR2ATD5mu1NA62YiGFx/ueYodFN4OhEw9AgRzlxOxpiERPAe45lvHlAli/zxCYPb6tgqMSGmkoAakCb4C7yN9Ornf8H5THLPTUL opensync_user@revssh-opensync.io" \
tunnel_remote_bind_port:=30337
Node is trying to connect to RevSSH server:
root@opensync:~# ovsh s RevSSH tunnel_status
----------------------------
tunnel_status | connecting |
----------------------------
root@opensync:~#
root@opensync:~# ovsh s RevSSH node_pubkey
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
node_pubkey | ssh-rsa AABBB3NzaC1yc2EAAAADAQABAAABAQDq4mtRBkZjs+fZffx8wOklXRNn6uoi3ySncz9GGmaVAxy9gIZWSqMzPsedu1K3PELQ4og2u/BVGknhZno9R2fIXzp33gNECyK8E/XogTg1XROMUKv9lLAySuKQz9YpP1iWShFyIDJf8sd4E/XA78M/Attm9Mj1mRVZ2s4HkF9CewSs+99uxvml66XYDV7oVQY4/Zhmcc1NzT8CQ21/7g+yRMdMblvxeZN40iUPcnglMto6MIqvjmAkLTWkidaWijha6GxT8wpo1ga3x4wEsAHZXtj15/lazvycaj3+1QyN8DdClWXsltdVOjVWZ6PwwFq5ea/glgh5/abcdkyVzPkf root@opensync |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Now, on RevSSH server add node's public key to authorized_keys
list:
Once the node's public key is added to RevSSH server's authorized_keys
list, the node successfully connects and establishes the revSSH tunnel:
Now, on the RevSSH server, user SSH-s to 127.0.0.1, port 30337 to SSH back to the OpenSync node through the established reverse SSH tunnel:
Tunnel status transitions to "active":
A more comprehensive OVSDB config example using Plume's RevSSH server, setting all the tunnel parameters, custom tunnel_local_port to 40337, custom session_max_time to 120 minutes, idle_timeout to 10 minutes and using temporary generation of node's keypair of type Ed25519 size 256 bits (Ed25519 has fixed size, so no need to specify size):