In December I was asked to look for a Digital Signage solution and after some research I came across Anthias. Anthias is the open sourced and free "brother" of Screenly, the "professional" solution for Digital Signage.
The easiest and fastest way to install Anthias is by using the already prepared Image for Raspberry Pi, which can be selected in the Raspberry Pi Imager software (Choose OS -> Other specific-purpose OS -> Anthias):
Note: The Anthias documentation and community recommends the installation using the "script" way, not using the Raspberry Pi image, as the image is outdated. For a POC it's OK though.
After using Anthias as "OS" and writing this onto a microSD card, it was time to boot up the Raspberry Pi and test Anthias.
The selected Anthias image itself is not installing a typical Raspbian Linux (a Debian-based Linux specific for Raspberry Pi's) as I expected, it installs an appliance-like OS called BalenaOS. This BalenaOS uses Docker containers to start up the application(s).
As I wanted to see how the containers are started, how they communicate and what kind of processes are running, I wanted to SSH into the Raspberry Pi. But... there was no SSH?
This is by design, as BalenaOS (by default) does not behave as a typical Linux system and doesn't expect anyone to log in on the device using SSH. However there's still a possibility as described in the BalenaOS documentation by enabling SSH in the BalenaOS configuration. But where is this configuration?
After the Anthias image was written to the SD card, the following partition layout can be seen (/dev/sdf represents the microSD card here):
ck@mint ~ $ sudo fdisk -l /dev/sdf
Disk /dev/sdf: 29.31 GiB, 31460950016 bytes, 61447168 sectors
Disk model: SD/MMC
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x3c736e59
Device Boot Start End Sectors Size Id Type
/dev/sdf1 * 8192 90111 81920 40M c W95 FAT32 (LBA)
/dev/sdf2 90112 745471 655360 320M 83 Linux
/dev/sdf3 745472 1400831 655360 320M 83 Linux
/dev/sdf4 1400832 8326566 6925735 3.3G f W95 Ext'd (LBA)
/dev/sdf5 1409024 1449983 40960 20M 83 Linux
/dev/sdf6 1458176 8326566 6868391 3.3G 83 Linux
My Linux Mint automatically mounted most partitions:
ck@mint ~ $ mount|grep media
/dev/sdf6 on /media/ck/resin-data type ext4 (rw,nosuid,nodev,relatime,uhelper=udisks2)
/dev/sdf2 on /media/ck/resin-rootA type ext4 (rw,nosuid,nodev,relatime,uhelper=udisks2)
/dev/sdf3 on /media/ck/resin-rootB type ext4 (rw,nosuid,nodev,relatime,uhelper=udisks2)
/dev/sdf5 on /media/ck/resin-state type ext4 (rw,nosuid,nodev,relatime,uhelper=udisks2)
/dev/sdf1 on /media/ck/resin-boot type vfat (rw,nosuid,nodev,relatime,uid=1000,gid=1000,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,showexec,utf8,flush,errors=remount-ro,uhelper=udisks2)
The interesting partition here is the one labelled "resin-boot" (/dev/sdf1). It contains the boot files for BalenaOS and also a configuration file (config.json):
ck@mint /media/ck/resin-boot $ ls -la
total 6728
drwxr-xr-x 6 ck ck 2048 Jan 1 1970 ./
drwxr-x---+ 9 root root 4096 Nov 22 19:36 ../
-rw-r--r-- 1 ck ck 24 Nov 30 2022 balena-image
-rw-r--r-- 1 ck ck 16915 Nov 30 2022 balenaos.fingerprint
-rw-r--r-- 1 ck ck 51675 Nov 30 2022 bcm2711-rpi-400.dtb
-rw-r--r-- 1 ck ck 51543 Nov 30 2022 bcm2711-rpi-4-b.dtb
-rw-r--r-- 1 ck ck 52116 Nov 30 2022 bcm2711-rpi-cm4.dtb
-rw-r--r-- 1 ck ck 516 Nov 30 2022 boot.scr
-rw-r--r-- 1 ck ck 137 Nov 30 2022 cmdline.txt
-rw-r--r-- 1 ck ck 581 Dec 12 2022 config.json
-rw-r--r-- 1 ck ck 36287 Nov 30 2022 config.txt
-rw-r--r-- 1 ck ck 2068 Nov 30 2022 device-type.json
-rw-r--r-- 1 ck ck 0 Nov 30 2022 extra_uEnv.txt
-rw-r--r-- 1 ck ck 3145 Nov 30 2022 fixup4cd.dat
-rw-r--r-- 1 ck ck 5354 Nov 30 2022 fixup4.dat
-rw-r--r-- 1 ck ck 8356 Nov 30 2022 fixup4x.dat
-rw-r--r-- 1 ck ck 44 Nov 30 2022 image-version-info
-rw-r--r-- 1 ck ck 591288 Nov 30 2022 kernel8.img
-rw-r--r-- 1 ck ck 176 Nov 30 2022 os-release
drwxr-xr-x 2 ck ck 22016 Nov 30 2022 overlays/
-rw-r--r-- 1 ck ck 0 Nov 30 2022 rpi-bootfiles-20220120.stamp
drwxr-xr-x 2 ck ck 512 Nov 30 2022 splash/
-rw-r--r-- 1 ck ck 800028 Nov 30 2022 start4cd.elf
-rw-r--r-- 1 ck ck 2240608 Nov 30 2022 start4.elf
-rw-r--r-- 1 ck ck 2992584 Nov 30 2022 start4x.elf
drwxr-xr-x 2 ck ck 512 Nov 30 2022 system-connections/
drwxr-xr-x 2 ck ck 512 Nov 22 2023 'System Volume Information'/
The default config.json looks something like this:
ck@mint /media/ckadm/resin-boot $ more config.json
{
"applicationId": 1971389,
"deviceType": "raspberrypi4-64",
"userId": 1375,
"appUpdatePollInterval": 600000,
"listenPort": 48484,
"vpnPort": 443,
"apiEndpoint": "https://api.balena-cloud.com",
"vpnEndpoint": "cloudlink.balena-cloud.com",
"registryEndpoint": "registry2.balena-cloud.com",
"deltaEndpoint": "https://delta.balena-cloud.com",
"mixpanelToken": "9ef939ea64cb6cd8bbc96af72345d70d",
"apiKey": "T4O0nRhnBSmevdnUf1gyFO7QFd9In3mr",
"files": {
"network/network.config": "[service_home_ethernet]\nType=ethernet\nNameservers=8.8.8.8,8.8.4.4"
}
}
The BalenaOS documentation mentions to add the os.sshKeys array into config.json. Let's do this with my own SSH public key and edit the config.json file (you need to be root to do that, unless you mounted the boot partition differently):
ck@mint /media/ckadm/resin-boot $ sudo vi config.json
After the change, the config.json content looks like this:
ck@mint /media/ckadm/resin-boot $ cat config.json
{
"applicationId": 1971389,
"deviceType": "raspberrypi4-64",
"os": {
"sshKeys": [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDUHd13XGHRnVUL8e/T7/zVJk385z4f+NlD0QvfA6t1LwGmsl6yvQK8MVnfAwSqv7HHzMRzd2CJfu4/uBrV6S1QVdtohYb3CU6UVxVLej+9pXEobWYrGV/h7FLna0C5okCAp/X0ZJtC9Mg3BmJYT3o+4D/hxvDVBYRhamDvMdF5edlK2dpqDaU706ktbHvB7DEx7AQcxZW4HxJT17H3OCHXYanpbn6gKpIXPGChGXbpMQAVQAxHe7ark7SiCzOR59UPGcWTUDrp4qMiUn3ZyLWuKWACEcjU/GzuOkmV80jEKMmv8SaYfHBsHeYLS2kvaHf/vvAqAvROXx5zyS2GBxV3 ck@claudiokuenzler.com"
]
},
"userId": 1375,
"appUpdatePollInterval": 600000,
"listenPort": 48484,
"vpnPort": 443,
"apiEndpoint": "https://api.balena-cloud.com",
"vpnEndpoint": "cloudlink.balena-cloud.com",
"registryEndpoint": "registry2.balena-cloud.com",
"deltaEndpoint": "https://delta.balena-cloud.com",
"mixpanelToken": "9ef939ea64cb6cd8bbc96af72345d70d",
"apiKey": "T4O0nRhnBSmevdnUf1gyFO7QFd9In3mr",
"files": {
"network/network.config": "[service_home_ethernet]\nType=ethernet\nNameservers=8.8.8.8,8.8.4.4"
}
}
After un-mounting the partitions and placing the microSD card back into the Raspberry Pi and booting it, it was time to find out, whether SSH would work now.
You would probably expect that the SSH daemon running in BalenaOS would listen to port 22. But nope! That's another important information which was discovered during my research: The SSH port is tcp/22222 on BalenaOS.
With this information at hand, let's try it:
ck@mint ~ $ ssh -p 22222 192.168.15.16
The authenticity of host '[192.168.15.16]:22222 ([192.168.15.16]:22222)' can't be established.
ECDSA key fingerprint is SHA256:EVwYGpGy7JkeNSRWHe2k5HhX3nETlJKwoX2ZFmhzxf0.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '[192.168.15.16]:22222' (ECDSA) to the list of known hosts.
X11 forwarding request failed on channel 0
root@621b11c:~#
Yes, I'm in!
I expected a docker command to be available, but that isn't the case inside BalenaOS. But there is the balena-engine command, which can be seen as a 1:1 replacement (or alias) of the docker command. So now I can see the Anthias containers and can also check their logs etc.
root@621b11c:~# balena-engine ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
72d44316442a 2b29aa41162a "/usr/bin/entry.sh n…" 36 seconds ago Up 32 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp anthias-nginx_7391455_2756077_0e2032b8d7b2743c2d0f4f25bdf2e212
05a9feb29604 4220520c41ee "/usr/bin/entry.sh b…" 36 seconds ago Up 1 second anthias-wifi-connect_7391449_2756077_0e2032b8d7b2743c2d0f4f25bdf2e212
5da0468d9a3b 6bcd295754fb "/usr/bin/entry.sh /…" 41 seconds ago Up 37 seconds anthias-celery_7391453_2756077_0e2032b8d7b2743c2d0f4f25bdf2e212
dfabdbd04fef 2dae5c417d1b "/usr/bin/entry.sh p…" 41 seconds ago Up 38 seconds anthias-websocket_7391452_2756077_0e2032b8d7b2743c2d0f4f25bdf2e212
a79df6c83e0c 793c0159fc03 "/usr/bin/entry.sh b…" 41 seconds ago Up 37 seconds anthias-viewer_7391451_2756077_0e2032b8d7b2743c2d0f4f25bdf2e212
a4e5ba719e76 3cbd1d68e67f "/usr/bin/entry.sh r…" 44 seconds ago Up 41 seconds 127.0.0.1:6379->6379/tcp redis_7391454_2756077_0e2032b8d7b2743c2d0f4f25bdf2e212
801349f361c2 97ee189d56f1 "/usr/bin/entry.sh b…" 44 seconds ago Up 41 seconds anthias-server_7391450_2756077_0e2032b8d7b2743c2d0f4f25bdf2e212
6199289e8b2d registry2.balena-cloud.com/v2/a8dcd97d7ce4e1702742090a1935fd7a "/usr/src/app/entry.…" 8 minutes ago Up 6 minutes (healthy) balena_supervisor
That's great to continue my work on the Digital Signage proof of concept project. It will help me to figure out technical details I need to know for security and additional configurations such as reverse proxying Anthias, and more.
CK from Switzerland wrote on Oct 30th, 2024:
You are welcome, Marc. Have a look at a related article I wrote on my company blog (Digital Signage: Open Source vs. Closed source solutions) where I describe the same frustration.
Marc from NL wrote on Oct 30th, 2024:
Thank you for your response! I will check it out. I wonder if the 'BalenaOS-strategy' for Anthias will open new worlds of possibilities, but so far it only frustrates me ;-) Cheers!
CK from Switzerland wrote on Oct 28th, 2024:
Marc, to create a SSH key pair (a private key and a public key) you can launch ssh-keygen -t ed25519 -C "Marc's key". This will create you a private key (which you can securely encrypt with your own personal password) and a public key you copy to the servers/systems you want to connect to. In case your OS is Windows, you can follow the Microsoft howto for Putty.
Marc from NL wrote on Oct 27th, 2024:
You have made it a beautiful study. Thanks! The step when inserting your public ssh key seemed a bit too quick to me. Where or how can you retrieve or create it?
AWS Android Ansible Apache Apple Atlassian BSD Backup Bash Bluecoat CMS Chef Cloud Coding Consul Containers CouchDB DB DNS Database Databases Docker ELK Elasticsearch Filebeat FreeBSD Galera Git GlusterFS Grafana Graphics HAProxy HTML Hacks Hardware Icinga Influx Internet Java KVM Kibana Kodi Kubernetes LVM LXC Linux Logstash Mac Macintosh Mail MariaDB Minio MongoDB Monitoring Multimedia MySQL NFS Nagios Network Nginx OSSEC OTRS Office PGSQL PHP Perl Personal PostgreSQL Postgres PowerDNS Proxmox Proxy Python Rancher Rant Redis Roundcube SSL Samba Seafile Security Shell SmartOS Solaris Surveillance Systemd TLS Tomcat Ubuntu Unix VMWare VMware Varnish Virtualization Windows Wireless Wordpress Wyse ZFS Zoneminder