This is the second installment of my article about the Serial Console Server for the Poor. First installment here.
The last part of the article having covered the hardware and the udev part creating the device nodes, this part addresses the part of the software that connects the user to the device node.
The idea of the software side is to allow users access only to certain serial ports without giving out a shell account on the server itself. Communication between user and the console server should be encrypted (which certainly means ssh), and users should be able to authenticate both with a key and with a password. Serial parameters (baud rate, parity, stop bits etc) should be configured on the console server to keep this possible error source away from the users.
For years, I have been thinking about using UUCP's cu to access the serial port. This time, it's not going to be used because it offers a shell escape which cannot be turned off. That would give a user shell to the users indirectly, which is not what I want.
Minicom seems still to be the most-used application to access a serial port. I have never understood the sense of using a terminal emulator inside a terminal emulator, especially if one has to break the terminal emulator's habit of trying to initialize a non-existent modem.
So I settled on the application that I usually use to access a serial port: ser2net. This is a Linux implementation of Cisco's "reverse telnet" feature which "connects" a TCP port to a serial port with a given set of parameters. For example,
connects TCP port 4016 with /dev/USBserial6 with an 38400 8n1 parameter set. If you telnet to localhost 4016, you'll find yourself talking to the device connected to the appropriate serial port.4016:telnet:600:/dev/USBserial6:38400 8DATABITS NONE 1STOPBIT banner
This moves the security risk to the telnet client, which - unfortunately - offers a shell escape function from the telnet command line reachable with the escape character. The only way to remove this is to use the -E command line option, which turns off the escape function completely. This unfortunately excludes the user from cleanly quitting the telnet session, so the only way to get out of a telnet -E is to have the remote side close the serial port (which ain't happening on a serial console) or to kill the telnet process, for example by closing the carrying ssh session. That's a turn-off, but one that one can live with.
Now, the only challenge left is to have the appropriate telnet client started automatically for the user. I decided on connecting this to the account being connected to, so that ssh USBserial4@hostname will automatically do the right thing. Since password authentication needs to work (optionally), this precludes the use of the command modifier in an authorized_keys file.
Another possibility to force a command being executed on login is to set it as the users' login shell in /etc/passwd. One drawback: The command must not have any parameters, so one needs to pass needed parameters to the program some other way. Additionally, the program must be listed in /etc/shells or it will be silently ignored.
My /etc/passwd entries look like
with /usr/local/sbin/serialconnect-shell looking likeUSBserial2:x:1002:1002:USBserial2,,,:/home/USBserial2:/usr/local/sbin/serialconnect-shell
Just for reference, here are the appropriate entries in /etc/ser2net.conf:#!/bin/bash set -eu case "$(id --user --name)" in USBserial1) PORT="2011";; USBserial2) PORT="2012";; USBserial3) PORT="6013";; USBserial4) PORT="6014";; USBserial5) PORT="2015";; USBserial6) PORT="2016";; USBserial7) PORT="6017";; *) echo "called from unknown account, terminating."; exit 1;; esac telnet -E localhost $PORT
2011:telnet:600:/dev/USBserial1:9600 8DATABITS NONE 1STOPBIT banner 2012:telnet:600:/dev/USBserial2:9600 8DATABITS NONE 1STOPBIT banner 6013:telnet:600:/dev/USBserial3:115200 8DATABITS NONE 1STOPBIT banner 6014:telnet:600:/dev/USBserial4:115200 8DATABITS NONE 1STOPBIT banner 2015:telnet:600:/dev/USBserial5:9600 8DATABITS NONE 1STOPBIT banner 2016:telnet:600:/dev/USBserial6:9600 8DATABITS NONE 1STOPBIT banner 6017:telnet:600:/dev/USBserial7:115200 8DATABITS NONE 1STOPBIT banner
If one wants to authenticate for a given serial port with an ssh key, all you need to do is put the appropriate key into the authorized_keys file of the appropriate account, and you're all set.
I do sincerely hope that I didn't put any blatant security goofs into this setup, but if I did, you'll tell me in the comments, right?
Just one more challenge for my valued readers: The port numbers in my ser2net.conf have a system. Can you explain it to me?