icon |
---|
engine |
{% hint style="info" %} This document describes version 5.6.x Other versions use a different method of forming the connection. {% endhint %}
There are four atSigns involved - one for each of
- the noports daemon program (
sshnpd
) which runs on the device you want to ssh to - the noports client programs (
sshnp/npt
) which you run on the device you want to ssh from - the noports tcp rendezvous program (
sshrvd
) - if required a policy engine
The programs communicate via the atProtocol and the atClient SDKs; as a result, the payloads of the messages the programs send to each other are all end-to-end encrypted.
In brief
- The client (
sshnp/npt
) creates a unique guid for the session- and sends a request notification to the
sshrvd
for a port1/port2 pair for this sessionId
- and sends a request notification to the
- The sshrvd
- finds a pair of available ports
- opens server sockets for both of them
- Note: rvd will allow just a single client socket to connect to each server socket
- and will bridge them together
- Note: rvd will allow just a single client socket to connect to each server socket
- sends response to the client
- The client
- receives the response notification from sshrvd (rv_host, rv_port_1, rv_port_2)
- and sends a request notification to the
sshnpd
including the sessionId and the rv_host:rv_port_1 and a new ephemeral AES 256 key
- The daemon (
sshnpd
)- opens a socket to the rv_host:rv_port_1 and authenticates
- and opens a socket to its local sshd port
- and bridges the sockets together whilst also encrypting data towards the rv_host
- and sends a response notification to the
sshnp
client
- The client
- Listens on a specified port and on connection encrypts traffic received with the AES key and forward on to the rv_host
- The client displays a message to the user that they may now
ssh -p $local_port $username@localhost
, i.e.ssh -p 58358 gary@localhost
in the example above, and exits
This high-level flow is visualized in the diagrams below.
NB Requests from unauthorized client atSigns are ignored. Note that one may also completely prevent requests from any other atSigns ever even reaching the daemon by using the atProtocol's config:allow
list feature.
In the personal edition of noports, a daemon may have only a single authorized client atSign.
The Team and Enterprise editions will allow for multiple authorized client atSigns, controlled not by the daemon but by a separate noports authorization controller process, with its own atSign.
At any point the sshnpd
or the srvd
software rather than using a local configuration to manage access rights, can forward those questions to another atSign. That atSign can in turn pass those queries to a policy engine and reflect the answer back to the asking atSign. In the example above @relay and/or @server could ask if @client is allowed to access the service. This allows decisions to be made at the Policy plane level and provides operational segregation of duties.
In the following sequence diagram, atServer address lookup flows, authentication flows, key exchange flows, precise encryption mechanics and notification transmission flows are not covered in detail; those details are provided in the links provided in the "Further Details" section below.
Since the full details are provided in those other links, the client_1 -> atServer_1 -> atServer_2 -> client_2
message flows are abbreviated to @atSign_1 -> @atSign_2
in the sequence diagram. Thus, for example, sshnp (@client)
encapsulates both the sshnp program and the sshnp atServer
sequenceDiagram
participant c as client
participant cs as client atServer
participant rs as relay atServer
participant r as relay
participant ds as daemon atServer
participant d as daemon
note over c,d: Phase - Background services startup
par
r ->> rs: connect
r ->> rs: pkam authenticate
r ->> rs: monitor for notifications
and
d ->> ds: connect
d ->> ds: pkam authenticate
d ->> ds: monitor for notifications
end
note over c,d: Phase - Ping daemon
c ->> cs: connect & pkam authenticate
c ->> cs: monitor for notifications
alt
c ->> d: ping the daemon
activate d
else actual data flow
c -->> cs:
cs -->> ds:
ds -->> d:
end
note over c,d: Phase - Request relay session
alt
c ->> r: request two public ports from relay: {session id, nonce from client}
else actual data flow
c -->> cs:
cs -->> rs:
rs -->> r:
end
r ->> r: Request ephemeral ports from OS
alt
r ->> c: ports response from relay: {session id, host1:port1, host2:port2, nonce from relay}
else actual data flow
r -->> rs:
rs -->> cs:
cs -->> c:
end
note over c,d: Phase - Receive ping response from daemon: {list of available features}
alt
d ->> c: ping response (device info / features) from daemon
deactivate d
else actual data flow
d -->> ds:
ds -->> cs:
cs -->> c:
end
c ->> c: Validate request against daemon ping info
note over c,d: Phase - Request daemon session
c ->> c: generate new aes encryption key & iv nonce
alt
c ->> d: send session request to the daemon: {host2:port2 from relay, aes stream encryption key, aes iv nonce, requested service to access (host:port), nonce from relay, nonce from client}
else actual data flow
c -->> cs:
cs -->> ds:
ds -->> d:
end
d ->> d: Verify perimissions for client's request (based on session request info - i.e. is client allowed to connect?)
d -x r: [TCP - A] Reverse TCP the requested service to open relay host2:port2
d -x r: [TCP - A] Send auth string (signed json payload of: session id, nonce from relay, nonce from client)
r ->> r: [TCP - A] Verify auth string
alt
d ->> c: session (success) response from daemon
else actual data flow
d -->> ds:
ds -->> cs:
cs -->> c:
end
note over c,d: Phase - Client TCP connect
c ->> c: Bind a local socket to expose the service on
c -x r: [TCP - B] TCP connect to the other open relay port
c -x r: [TCP - B] Send auth string (signed json payload of: session id, nonce from relay, nonce from client)
r ->> r: [TCP - B] Verify auth string
note over c,d: Phase - internal traffic relays (lasts entire duration of the session)
par
r -->> r: relay all traffic from [TCP - A] to [TCP - B] and vice-versa
and
c -->> c: relay all traffic from [TCP - A] to local socket and vice-versa
end
note over c,d: Phase - using the connection
c ->> c: You connect to the local socket as if it were the remote service!
Once the interactions above have completed
- the sshnpd nor the sshnp programs are no longer involved
- there is a new sshrv process running on the device host which pipes i/o between requested server:port and $rv_host:$rv_port_1
- there is a client process running on the client host which provides the local port forwarding tunnel
- User may now type "ssh -p $local_port username@localhost" or use a client application like and RDP client with traffic flowing
- client ssh program <===>
- $client_localhost:$local_port <===> bridged by client-side ssh tunnel to
- $rv_host:$rv_port_2 <===> bridged by sshrvd to
- $rv_host:$rv_port_1 <===> bridged by device-side sshrv to
- $device_host:22 <===>
- device sshd program
- $device_host:22 <===>
- $rv_host:$rv_port_1 <===> bridged by device-side sshrv to
- $rv_host:$rv_port_2 <===> bridged by sshrvd to
- $client_localhost:$local_port <===> bridged by client-side ssh tunnel to
- client ssh program <===>
In the sections above, we referred to "authentication", "sending notifications" and "receiving notifications", and we made the statement that "the payloads of the messages the programs send to each other are all end-to-end encrypted"
Here are some links to detailed diagrams covering
- how atClients authenticate to their atServers
- how encrypted data is exchanged (including how keys are exchanged)
- how notifications work