Skip to content

Serial Console Server for the Poor III

This is the third installment of my article about the Serial Console Server for the Poor. First installment here, Second installment here.

The first part of the article having covered the hardware and the udev part creating the device nodes, and the second part explaining how to solve the software part using ser2net, this part explains why ser2net was ditched in favor of cereal and how the console server operates with cereal now.

Cereal (which I was pointed to on IRC as a reaction to article II) is a nifty combination of runit's runsv and screen. One of the less-known features of screen is that screen can also connect to a serial port instead of a "real" console. cereal makes use of this feature, putting up with the fact that screen can only set a baud rate. I have not seen a serial application with other parameters than 8n1 (8 data bits, no parity, 1 stop bit) in a decade, so I cannot comment whether this is a real disadvantage. I guess that it might be possible to set the serial port to the desired set of parameters before starting up screen, but I didn't actually try it since I do not need it.

Cereal uses a set of configuration parameters and processes called a "session" to associate a serial port with a baud rate and a set of user/group settings to control who is able to start, stop, attach or detach a session. It doesn't have a "real" configuration file. Instead, it has an admin program, cereal_admin, which takes the parameters associated with a session and writes them to an (intentionally? undocumented?) set of config files in /var/lib/cereal. Only a single user is able to attach to a session read/write, while an entire group can attach to a session read-only and can thus access the logs. A session is automatically started when the sysem boots and can be stopped and started using cereal_admin as well. Once a session is running, a specially configured screen process runs attached to the serial port, and users simply attach and detach to and from it as one would to a normally running screen. That way, one can scroll back and examine what was shown on the serial port while nobody was attached. This is quite handy when the device connected to the port prints out warnings, errors and status information to its serial port.

The screen configuration that is used is rather sophisticated. So, a permanent log file is written, which is also used for read-only access to the serial session. Also, a very informative status line is configured.

Unfortunately, one of screen's biggest disadvantages applies here as well: User X cannot attach to a session started by User Y unless Y has access privileges for X's pty (or vice versa, I never can remember), which can be a security risk. Additionally, it is not possible to su or sudo to X and then to attach to the session - one needs to have the original pty opened by the correct user, which usually means that one needs to do a "real" log in with the correct account (via /bin/login or sshing in to the box).

If one has the privileges necessary, one can use the cereal executeable to attach to the screen (which gives read-write access) or to tail -F the log file (giving timestamped read-only access). That way, one never knows that one is actually working with an appropriately configured screen that is kept running by runit.

The restrictions coming with cereal suggest a setup similiar to what I did with ser2net previously: Each session has its own user account, which forces an attach to the appropriately named cereal shell on login:

$ cat /usr/local/sbin/cerealshell

set -eu

cereal attach $(id --user --name)

You can see that I decided to name the sessions after the actual device name, allowing the unterlying ttyUSB device name to change without change in the user interface. The actual users and sessions were created with a small shell script:


createconsole() {
  echo sudo addgroup --force-badname --gid $MUID $NAME
  echo sudo adduser --force-badname --gecos "$NAME,,," --ingroup $NAME --uid $MUID --disabled-password $NAME
  echo sudo adduser $NAME dialout
  echo sudo chsh --shell=/usr/local/sbin/cerealshell $NAME
  echo sudo mkdir -p /home/$NAME/.ssh
  echo sudo chmod 755 /home/$NAME/.ssh
  echo sudo touch /home/$NAME/.ssh/authorized_keys
  echo sudo chmod 644 /home/$NAME/.ssh/authorized_keys
  echo sudo chown $NAME:$NAME /home/$NAME/.ssh /home/$NAME/.ssh/authorized_keys
  echo sudo cereal-admin create $1 /dev/$2 $3 $1 $1
  echo sleep 2
    echo sudo cereal-admin start $1
  echo echo -----

createconsole my-device-1 USBserial1 9600
createconsole my-device-2 USBserial3 115200
Note that the script also uses a hardcoded UID range which might need adaption to your local needs (and breaks for more than ten serial ports), and that it also takes care of creating an empty authorized_keys file with correct permissions.

That way one can simply ssh to my-devlce-1@consoleserver and find oneself immediately connected to the serial console, seeing the last things that happened on the serial port, and immediately being able to work with the device. That's fine, comfortable and useable. I actually like that better than what I built with ser2net previously.


No Trackbacks


Display comments as Linear | Threaded

Np237 on :

Have you ever tried conman [0]? It is a very good tool for this job of storing the logs and allowing login to consoles for users. It is generally used to access consoles with IPMI serial-over-ethernet, but you can use a custom script to access the console.


Add Comment

Markdown format allowed
Enclosing asterisks marks text as bold (*word*), underscore are made via _word_.
Standard emoticons like :-) and ;-) are converted to images.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications.
Form options