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.
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.
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.
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 ...
First, set up the Linux machine to which the serial console is connected.
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 /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
For systemd, modify the operation of bluetooth.service when it starts, and set it to display a login prompt when connecting serially.
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
.
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.
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]
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.
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.
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.
rfcomm
to spawn child processes rfcomm
launches ```agetty`` as a child process, it will transition its domain to "getty_t". rfcomm
and ```agetty`` related processesOf 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
).
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.
This will allow `ʻagetty`` to work according to the default authorization 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
At this point, reboot the machine and the serial console should be in standby via Bluetooth.
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.
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.
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.
Describes how to pair from a Windows machine. Of course, the procedure is different, but you can also connect from another OS.
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).
You can connect to the serial console by opening the port assigned to "Outgoing" here (COM4 in this example).
The quickest way to open a COM port is to use Tera Term, which you probably use all the time.
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.
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!
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)
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.