SSH¶
All SBC edge clients can be accessed using SSH. Every authentication type is supported. Our preinstalled edge clients require a "24 hour Mellon certificate" with the principal building-admins.
OpenSSH¶
You can also use OpenSSH without "c1-ssh" utilizing the ProxyCommand option. In addition to OpenSSH, socat and curl are required
On Ubuntu these can be installed by executing:
apt install socat curl
Now place the following script in ~/.ssh/sensaru-proxy.sh:
#!/usr/bin/env bash
# Example usage: ssh -o ProxyCommand="~/.ssh/sensaru-proxy.sh %h" -o UserKnownHostsFile=/dev/null root@2007c641-b994-443e-b827-1748da6d5dd0_1234_1_10000
business_partner_id=$(echo $1 | cut -d '_' -f 1)
device_id=$(echo $1 | cut -d '_' -f 2)
sub_id=$(echo $1 | cut -d '_' -f 3)
client_index=$(echo $1 | cut -d '_' -f 4)
# Get hostname
hostname=$(curl -s "https://edge.sensaru.cloud/?c1pclientindex=${client_index}&c1pbp=${business_partner_id}&c1pdeviceid=${device_id}&c1psubid=${sub_id}&c1pproxymode=1&c1pgethost=1") > /dev/null 2>&1
if [ -z $hostname ]; then
>&2 echo "Could not determine hostname"
exit 1
fi
(printf 'GET /?c1pclientindex=%s&c1pbp=%s&c1pdeviceid=%s&c1psubid=%s&c1pproxymode=1&c1pnohttpresponse=1 HTTP/1.1\r\nConnection: keep-alive\r\n\r\n' $client_index $business_partner_id $device_id $sub_id && cat) | socat - SSL:$hostname:4027
Make this script executable:
chmod +x ~/.ssh/sensaru-proxy.sh
Add an ssh alias called sssh:
echo "alias sssh='ssh -o ProxyCommand=\"~/.ssh/sensaru-proxy.sh %h\" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'" >> ~/.bashrc
After a restart of your shell, you can SSH into any SBC edge client using:
sssh user@hostname
Replace hostname with <business partner ID>_<home client ID>_<sub ID>_<client index>. As a system provider or system distributor the hostname can be copied from https://sensaru.cloud in device management on the edge clients detail page (at the top).
SCP¶
Like for SSH add an alias:
echo "alias sscp='scp -o ProxyCommand=\"~/.ssh/sensaru-proxy.sh %h\" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'" >> ~/.bashrc
SCP can be used the same way:
sscp user@hostname:/my-file .
c1-ssh¶
The easiest way to SSH into edge clients is by using the tool "c1-ssh". c1-ssh is available precompiled in our APT repository for Debian and Ubuntu. It is just a small wrapper around the systems SSH client and establishes the connection to Sensaru Cloud. Using c1-ssh also has the advantage that you can always trust the connection. You don't get the usual SSH key warning on first connection and don't need to check it. This makes man-in-the-middle attacks on the SSH connection much, much harder.
To connect to an edge client, execute:
c1-ssh <user>@<business partner ID>_<economic unit ID>.<property ID>.<administration unit ID>_<sub ID>_10000
The property ID and administration unit ID are optional. The sub ID is usually 1. 10000 is the client index. This is the default value for SSH and can be changed in cloudconnect.conf on the client. So an example command might look like this:
c1-ssh root@0604b020-7905-11eb-ad7b-f9e2c6c59018_6261.107.97_1_10000
In addition you can pass most SSH parameters to c1-ssh, e. g. to tunnel connections.
SCP¶
To copy files you can use c1-scp which comes with c1-ssh.
Python¶
Here is an example Python script using Paramiko as SSH client.
#!/usr/bin/env python3
import sys
import socket
import ssl
import requests
import paramiko
from urllib.parse import urlencode
class ProxyCommand:
def __init__(self, host_string):
# Parse host string (format: ID_DEVICEID_SUBID_CLIENTINDEX)
parts = host_string.split('_')
if len(parts) != 4:
raise ValueError("Invalid host string format")
self.business_partner_id = parts[0]
self.device_id = parts[1]
self.sub_id = parts[2]
self.client_index = parts[3]
def get_hostname(self):
"""Get the actual hostname from the edge service"""
params = {
'c1pclientindex': self.client_index,
'c1pbp': self.business_partner_id,
'c1pdeviceid': self.device_id,
'c1psubid': self.sub_id,
'c1pproxymode': '1',
'c1pgethost': '1'
}
response = requests.get('https://edge.sensaru.cloud/', params=params)
if not response.ok:
raise RuntimeError("Could not determine hostname")
return response.text.strip()
def create_socket(self):
"""Create and return an SSL wrapped socket connected to the target"""
hostname = self.get_hostname()
# Create base socket
sock = socket.create_connection((hostname, 4027))
# Wrap with SSL
context = ssl.create_default_context()
ssl_sock = context.wrap_socket(sock, server_hostname=hostname)
# Send HTTP GET request
params = {
'c1pclientindex': self.client_index,
'c1pbp': self.business_partner_id,
'c1pdeviceid': self.device_id,
'c1psubid': self.sub_id,
'c1pproxymode': '1',
'c1pnohttpresponse': '1'
}
request = f"GET /?{urlencode(params)} HTTP/1.1\r\nConnection: keep-alive\r\n\r\n"
ssl_sock.send(request.encode())
return ssl_sock
def execute_ssh_command(host_string, username, command, password=None):
"""Execute a command over SSH using the proxy connection"""
proxy = ProxyCommand(host_string)
sock = proxy.create_socket()
# Create SSH client
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
# Connect using the socket
ssh.connect(
host_string,
username=username,
password=password,
sock=sock
)
# Execute command
stdin, stdout, stderr = ssh.exec_command(command)
# Print output
print("STDOUT:")
print(stdout.read().decode())
print("STDERR:")
print(stderr.read().decode())
finally:
ssh.close()
sock.close()
if __name__ == "__main__":
if len(sys.argv) < 4:
print("Usage: pysssh.py HOST_STRING USERNAME COMMAND [PASSWORD]")
sys.exit(1)
host_string = sys.argv[1]
username = sys.argv[2]
command = sys.argv[3]
password = sys.argv[4] if len(sys.argv) > 4 else None
execute_ssh_command(host_string, username, command, password)
Place this script into pysssh.py and execute with for example python3 pysssh.py 39183456-1927-48e8-ab79-237491b86216_RPIB827EB17ACF0_1_10000 pi date my-password.