[LINUX] Connect to a serial console via Bluetooth (CentOS 7)

This article will show you how to allow a CentOS 7 machine to have a Bluetooth serial console login from another device.

I think the method is a little different for other distributions, but I think it can be achieved in the same way.

Preface

When operating a Linux machine (especially for servers), you will often connect from another terminal via a network such as SSH. However, have you ever been unable to make an SSH connection by doing something like the following during an SSH connection?

-- /etc/init.d/network stop ( systemctl stop network) --I couldn't communicate with the SSH client after tweaking the network settings such as routing. --I have kill sshd

It is convenient to have the serial console enabled for this (?). You can log in via the serial console by connecting the serial ports of the Linux machine and the terminal machine. However, most modern PCs no longer have a serial port. [^ 1]

[^ 1]: It's a D-Sub 9-pin RS-232C. A long time ago? It has disappeared since about. There is also a USB-serial conversion cable, but it costs about 2,000 yen each. To connect machines that do not have a serial port, you need to connect a conversion cable to each and connect them with a serial cable. It costs money and I'm not the only one who thinks it's a bit nonsense.

That's where Bluetooth comes in. Although serial ports have disappeared from modern PCs, Bluetooth is included in many PCs (USB dongles are available at a relatively low price even if they aren't). And serial communication is also possible with Bluetooth. I tried to use this to make a serial console connection, so I will publish the procedure.

environment

Since the OS is CentOS 7 and the GUI desktop environment is selected at the time of installation, I think that some packages are not installed in minimal installation. Do yum install if necessary.

In addition, SELinux policy is created and incorporated so that SELinux can be used in a valid state. If you have SELinux disabled (Permissive), you can skip the steps in Creating and Applying SELinux Policies. [^ 2]

[^ 2]: Even in the case of Permissive, if you skip the SELinux procedure, a lot of Audit Logs will be output, so I think that there is no loss at last. I think it depends on the situation, but I think the era of unavoidably disabling SELinux is almost over.

Anyway, the procedure

First, make the settings on the Linux machine side, and then perform pairing with the terminal side machine. You can connect to the serial console only from the paired terminal.

It does not explain the details of Bluetooth-related terms and commands. Or rather, I did it myself, so I didn't understand it enough to explain ...

Linux machine settings

First, set up the Linux machine to which the serial console is connected.

Preparation: Check if Bluetooth is available

Anyway, if Bluetooth isn't available, the story won't start, so first check if it's available.

You can check it by doing grep on dmesg as follows.

$ grep -i bluetooth /var/log/dmesg
[    9.890172] Bluetooth: Core ver 2.22
[    9.890205] Bluetooth: HCI device and connection manager initialized
[    9.890211] Bluetooth: HCI socket layer initialized
[    9.890215] Bluetooth: L2CAP socket layer initialized
[    9.890223] Bluetooth: SCO socket layer initialized
[    9.930770] Bluetooth: hci0: read Intel version: 370810011003110e00
[    9.936104] Bluetooth: hci0: Intel Bluetooth firmware file: intel/ibt-hw-37.8.10-fw-1.10.3.11.e.bseq
[   10.271789] Bluetooth: hci0: Intel Bluetooth firmware patch completed and activated

Edit bluetoothd config file

Edit /etc/bluetooth/main.conf to enable AutoEnable. By default, Bluetooth is disabled at boot time on your Linux machine, but it will be enabled at boot time. [^ 3]

[^ 3]: If you want to enable / disable at any time after startup, you can do it with power on / power off with bluetoothctl.

The relevant part is at the bottom of the file.

/etc/bluetooth/main.conf


-AutoEnable=false
+#AutoEnable=false
+AutoEnable=true

systemd settings

For systemd, modify the operation of bluetooth.service when it starts, and set it to display a login prompt when connecting serially.

Add SPP at startup of bluetooth.service

Create a bluetooth.service.d directory under / etc / system / systemd / and create a Drop-in configuration file in it. I think that the file name can be almost anything as long as it ends with ".conf".

ini:/etc/system/systemd/bluetooth.service.d/add-spp.conf


[Service]
ExecStart=
ExecStart=/usr/libexec/bluetooth/bluetoothd --compat
ExecStartPost=/usr/bin/sdptool add SP

This configuration file adds an SPP (Serial Port Profile) for serial communication via Bluetooth when bluetoothd is started. You must start bluetoothd with the --compat (or -C) option before you can use sdptool.

rfcomm-getty unit file

Create a new file under / etc / system / systemd /.

/etc/system/systemd/[email protected]


[Unit]
Description=RFCOMM Getty on %I
Documentation=https://qiita.com/tetsuy/items/d9220ac66bd18fd0a01b
After=bluetooth.service
Requires=bluetooth.service
Before=getty.target
IgnoreOnIsolate=yes

[Service]
ExecStart=/bin/rfcomm watch %I 1 /sbin/agetty 115200,38400,9600 %I
UtmpIdentifier=%I
TTYPath=/dev/%I
TTYReset=yes
TTYVHangup=yes

[Install]
WantedBy=getty.target

rfcomm watch listens for a serial connection, and when connected, it creates an RFCOMM device (/ dev / rfcommX) and executes the specified command (starts a child process). By specifying to run `ʻagetty`` here and passing / dev / rfcommX to it, the login prompt will be displayed at the serial source.

Reflection of systemd settings

In addition, although it is a little off topic, there is an article that explains that editing the unit file of systemd is to edit the file under / usr / lib / systemd / system /, but the method is ** wrong. **is. (Files under this directory may be updated when systemd is updated)

Reference: [Red Hat Enterprise Linux 7 System Administrator's Guide / 10.6.4. Modifying Existing Unit Files](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/system_administrators_guide/ sect-Managing_Services_with_systemd-Unit_Files # sect-Managing_Services_with_systemd-Unit_File_Modify)

Returning to the story, once the above two files are created, they will be reflected and enabled in systemd.

# systemd daemon-reload
# systemd enable [email protected]

udev rule creation

Next, create a new rule file under /etc/udev/rules.d/.

config:/etc/udeb/rules.d/90-rfcomm.rules


KERNEL=="rfcomm[0-9]*", ENV{ID_MM_DEVICE_IGNORE}="1"

If you do not do this, ModemManager will react to the RFCOMM device file / dev / rfcomm0 created when you make a Bluetooth connection from the terminal and send data that looks like an AT command to the terminal (it looks like a login prompt) The character interrupts).

systemd {stop, disable} ModemManager is fine, but since it is a service that is enabled by default, I added an exclusion rule so that I will not get hooked at any time.

SELinux policy creation and enforcement

This time, as a result of various research related to SELinux, I was able to take a step from "I hate SELinux without eating". For those who don't like eating like me, I would like to write in more detail in the mountains, but I will omit it because the volume will increase too much.

Policy of policy to create

In a SELinux-enabled environment, manually launching rfcomm from the shell worked fine, but when I tried to launch it as a daemon from systemd, it didn't work. After investigating the cause, we found the following.

--The domain "unconfined_t" is given to the process started from the shell. This process can run without SELinux restrictions --On the other hand, when started as a daemon from systemd, the process's own domain ("bluetooth_t" in the case of rfcomm) is given by "domain transition". --Maybe because it is not supposed to launch and use `ʻagetty`` from the process of" bluetooth_t "domain, there is no policy to allow such operation by default.
→ ** SELinux rejects operation **

If there is no corresponding SELinux policy, I will create it myself, so I steadily crushed it using the `ʻausearch`` command in Permissive mode. The contents of the created policy are roughly the following four points.

  1. Allow rfcomm to spawn child processes
  2. When rfcomm launches ```agetty`` as a child process, it will transition its domain to "getty_t".
  3. Allow signals to be exchanged between rfcomm and ```agetty`` related processes
  4. Allow access to Bluetooth related resources (such as / dev / rfcomm0) from `ʻagetty`` related processes

Of these, the most important point is 2.

Access to login-related resources from the ʻagetty`` process is, of course, allowed by default. However, in this case, the operation is rejected. This is because the ʻagettyprocess runs in the" bluetooth_t "domain in this case, even though the permission policy is set on the assumption that it runs in the domain" getty_t ". (The child process takes over the domain of the parent process, which happens when rfcomm in the "bluetooth_t" domain invokes ```agetty).

ドメインを継承すると agetty がログイン関連のリソースを参照できない

Normally, when creating an SELinux policy, put it in Permissive mode and use the `ʻausearch`` command to write a rule that allows SELinux to be rejected one by one in the Audit Log. But if you do this straightforwardly in this case, you end up allowing processes in the "bluetooth_t" domain to access login-related resources. You'll probably write a lot of rules, and it doesn't look like security.

So I'm writing a domain transition rule so that when rfcomm launches `ʻagetty``, the process will be launched in the original" getty_t "domain.

ドメイン遷移によって agetty がログイン関連のリソースを参照できる

This will allow `ʻagetty`` to work according to the default authorization policy.

How to create and apply a policy

Well, the explanation is long, but it is how to create and apply a policy.

Install the selinux-policy-devel package, which is required to generate the SELinux policy file.

# yum install selinux-policy-devel

It also creates a te file (Type Enforcement) that is the basis of the policy file. The location is OK anywhere, but tentatively it is / root / selinux /. Running make will generate multiple files, including directories, so it is better to create a new dedicated directory.

/root/selinux/rfcomm-getty.te



module rfcomm-getty 1.0;

require {
        type bluetooth_t;
        type getty_exec_t;
        type getty_t;
        type local_login_t;
        class file { execute open read };
        class process { transition sigchld signal };
        class socket { read write };
}

allow bluetooth_t getty_exec_t:file { execute open read };
allow bluetooth_t getty_t:process { transition sigchld };
allow bluetooth_t local_login_t:process signal;
type_transition bluetooth_t getty_exec_t:process getty_t;
allow getty_t bluetooth_t:socket { read write };
allow getty_t bluetooth_t:process sigchld;
allow local_login_t bluetooth_t:process sigchld;
allow local_login_t bluetooth_t:socket { read write };

Generate a pp file (Policy Package) from this te file you created and apply that policy.

# cd /root/selinux
# make -f /usr/share/selinux/devel/Makefile rfcomm-getty.pp
/usr/share/selinux/devel/include/contrib/container.if:14: Error: duplicate definition of container_runtime_domtrans(). Original definition on 14.
...snip...
/usr/share/selinux/devel/include/contrib/container.if:498: Error: duplicate definition of container_runtime_typebounds(). Original definition on 684.
Compiling targeted rfcomm-getty module
/usr/bin/checkmodule:  loading policy configuration from tmp/rfcomm-getty.tmp
/usr/bin/checkmodule:  policy configuration loaded
/usr/bin/checkmodule:  writing binary representation (version 19) to tmp/rfcomm-getty.mod
Creating targeted rfcomm-getty.pp policy package
rm tmp/rfcomm-getty.mod.fc tmp/rfcomm-getty.mod
# ls
rfcomm-getty.fc
rfcomm-getty.if
rfcomm-getty.pp
rfcomm-getty.te
tmp

I get an error, but it's okay if rfcomm-getty.pp is generated.

Install the policy generated by semodule -i.

# semodule -i rfcomm-getty.pp

Machine reboot

At this point, reboot the machine and the serial console should be in standby via Bluetooth.

Bluetooth serial console connection from Windows

From here, the procedure is to pair the terminal machine to be connected to the Linux machine and log in with the serial console.

The Linux machine and the terminal machine must be paired in advance in order to make a serial console connection via Bluetooth. Conversely, it means that you cannot connect to the serial console via Bluetooth from a terminal that has not been paired in advance.

Pairing

In order for devices to communicate with each other via Bluetooth, they must first be paired. After making one device discoverable from surrounding devices, the other device will send a pairing request to that device.

Here is an example of pairing from a Windows 10 machine. [^ 4]

[^ 4]: On the contrary, I think that it can be used in the same way even if pairing is done from the Linux machine side (unverified), but I think that this is easier in terms of procedure.

Discover Linux machines

First, make the Linux machine discoverable from bluetoothctl.

# bluetoothctl
[NEW] Controller XX:XX:XX:XX:XX:XX HOSTNAME [default]
[bluetooth]# show
Controller XX:XX:XX:XX:XX:XX
        Name: HOSTNAME
        Alias: HOSTNAME
        Class: 0x000104
        Powered: yes
        Discoverable: no
        Pairable: yes
        UUID: Generic Attribute Profile (00001801-0000-1000-8000-00805f9b34fb)
        UUID: A/V Remote Control        (0000110e-0000-1000-8000-00805f9b34fb)
        UUID: PnP Information           (00001200-0000-1000-8000-00805f9b34fb)
        UUID: Generic Access Profile    (00001800-0000-1000-8000-00805f9b34fb)
        UUID: A/V Remote Control Target (0000110c-0000-1000-8000-00805f9b34fb)
        Modalias: usb:v1D6Bp0246d052C
        Discovering: no

At the bluetoothctl prompt, type show to see the controller information. If you confirm that "Powered: yes" and "Pairable: yes" are set and set "discoverable on", the Linux machine will be discoverable by Bluetooth for 3 minutes by default.

[bluetooth]# discoverable on
Changing discoverable on succeeded
[CHG] Controller XX:XX:XX:XX:XX:XX Discoverable: yes
[bluetooth]#

If "Pairable: no" is set, pairing will fail even if it can be found. Pairing is possible with pairable on.

Pairing is performed from the terminal machine side while it is discoverable.

Pairing with a Windows machine and COM port assignment

Describes how to pair from a Windows machine. Of course, the procedure is different, but you can also connect from another OS.

  1. In Windows "Settings"-> "Devices"-> "Bluetooth and other devices", make sure Bluetooth is turned on, and then "Add Bluetooth or other device"
  2. When the "Add Device" window opens, select "Bluetooth" and click the host name of the Linux machine that appears in the list.

I think this is all you need to do to complete the pairing (the bluetoothctl prompt on the Linux side will also show that the pairing has been completed).

In addition, check the serial port assignment. If you pair with SPP enabled, you will automatically be assigned two COM ports (incoming and outgoing).

  1. In the settings "Bluetooth and other devices", scroll down and click on "Other Bluetooth options" under "Related settings"
  2. The "Bluetooth Settings" window will open. Click the "COM Port" tab.

bluetooth_settings_comports.png

You can connect to the serial console by opening the port assigned to "Outgoing" here (COM4 in this example).

Serial console connection

The quickest way to open a COM port is to use Tera Term, which you probably use all the time. teraterm_serialport.png

When connecting with SSH, specify the host name and port, but select "Serial", select the COM port, and click "OK". only this. You should see a login prompt on the screen.

Unsolved points

When connecting via Bluetooth from the terminal, the following error log is output.

/dev/rfcomm0: cannot get controlling tty:It is an operation that is not permitted
/dev/rfcomm0: cannot get controlling tty:It is an operation that is not permitted
/dev/rfcomm0: cannot set process group:Inappropriate ioctl for device

I still don't understand the things around TTY ...

If you know the cause, please let me know!

reference

These are the sites and articles that I referred to. Thank you very much.

-I tried sending and receiving data to Raspberry Pi 2 via Bluetooth serial communication --Katakata Blog -Serial communication with Raspberry Pi via Bluetooth --mattintosh note -Until communication is possible via Bluetooth-- Qiita --Bluetooth connection with Linux command line --Qiita -Armadillo-X1: Serial communication with Bluetooth | Armadillo -A guide to eradicate "it doesn't work because of SELinux" --Qiita -Three points to understand SELinux security settings | Nikkei Crosstech (xTECH)

Bonus: Connect from Android terminal app

I use a terminal app called ConnectBot on Android, but this app does not support serial connection like Tera Term.

If you search for "Bluetooth serial" in the Play Store, you will find a lot of Bluetooth terminal apps, but none of them are as easy to use as the apps you use all the time.

Therefore! An app called "Bluetooth Bridge (+ TCP)" will appear.

This is an app that bridges the communication between two Bluetooth or TCP. One is a Bluetooth serial connection, the other is a TCP Server, listen on any port to bridge bidirectional communication, and then Telnet from the usual terminal app to the specified port on localhost.

I'm not a fan of this app, but it was a good job app, so I introduced it.

Recommended Posts

Connect to a serial console via Bluetooth (CentOS 7)
Using a serial console on Ubuntu 20.04
Connect to Packetix VPN from CentOS 7 minimal.
Connect to mysql
When you want to play a game via Proxy