moby: Bluetooth socket can't be opened inside container

Hello,

I’m trying to get my bluetooth dongle working inside a docker container. When I try to start bluetoothd it says

root@a28c9bc0b5ef:~# /usr/local/libexec/bluetooth/bluetoothd -n
bluetoothd[15]: Bluetooth daemon 5.34
bluetoothd[15]: Failed to access management interface
bluetoothd[15]: Adapter handling initialization failed

I traced down the code inside bluez which triggers this behavior (src/shared/mgmt.c:439):

    fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                BTPROTO_HCI);

The socket syscall always returns -1. When I “strace” the command I see

socket(PF_BLUETOOTH, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, 1) = -1 EAFNOSUPPORT (Address family not supported by protocol)

The same command works on the host machine without any issues. The bluetooth modules are all loaded:

root@a28c9bc0b5ef:~# lsmod | grep bt
btusb                  28346  0 
btbcm                   4204  1 btusb
btintel                 1332  1 btusb
bluetooth             315821  5 bnep,btbcm,btusb,btintel

/sys/class/bluetooth/hci0 exists both on the host as well as inside the container. It doesn’t matter how I run the container. Neither --privileged nor --cap-add=ALL nor --cap-add=NET_ADMIN change this behavior. A quick glance at linux/src/bluetooth/hci_sock.c reveals that CAP_NET_ADMIN and CAP_NET_RAW should be enough to issue any command to the socket (never had anything to do with linux kernel code until yet so I don’t know what exactly I’m talking about, though). I guess this is a problem with how docker(/lxc?) restricts the containers, but I don’t know for sure.

Obligatory bug report information:

# docker version
Client:
 Version:      1.8.1
 API version:  1.20
 Go version:   go1.4.2
 Git commit:   b66e5ef-dirty
 Built:        Wed Sep  2 19:02:45 UTC 2015
 OS/Arch:      linux/arm

Server:
 Version:      1.8.1
 API version:  1.20
 Go version:   go1.4.2
 Git commit:   b66e5ef-dirty
 Built:        Wed Sep  2 19:02:45 UTC 2015
 OS/Arch:      linux/arm

# docker info
Containers: 5
Images: 27
Storage Driver: aufs
 Root Dir: /var/lib/docker/aufs
 Backing Filesystem: extfs
 Dirs: 37
 Dirperm1 Supported: true
Execution Driver: native-0.2
Logging Driver: json-file
Kernel Version: 4.1.6-v7+
Operating System: Debian GNU/Linux 8 (jessie)
CPUs: 4
Total Memory: 925.7 MiB
Name: raspberry
ID: T3Z3:Z6BJ:IGU5:F7HP:AGPB:LZTR:VQH2:EZUJ:HTVR:FRFX:H2RT:4V3M
WARNING: No memory limit support
WARNING: No swap limit support

# uname -a
Linux raspberry 4.1.6-v7+ #1 SMP PREEMPT Sun Aug 23 22:01:27 CEST 2015 armv7l GNU/Linux

Environment details: Physical (Raspberry Pi 2) How reproducible: Always Steps to reproduce:

  1. Build container with bluez installed
  2. Run with run options mentioned above (parameters don’t seem to matter anyway)
  3. Start bluetoothd Actual results: Doesn’t start up because it cannot open bluetooth socket Expected results: bluetoothd starts up just fine (at least when run with the right capabilities enabled)

PS1: This also happens on amd64 PS2: This also happens when using the --device switch

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Reactions: 11
  • Comments: 55 (6 by maintainers)

Commits related to this issue

Most upvoted comments

Is there any way to access the bluetooth hardware without --net=host?

It seems that the linux kernel currently does not allow containers to create bluetooth sockets (see this kernel patch for more info https://patchwork.kernel.org/patch/9898285/).

One workaround would be to switch bluetooth processes inside the containers back to the host net namespace without impacting the rest of the containers processes (eg. sshd).

To do that:

  1. bind mount /proc/1/ns/net inside the container
docker run --mount type=bind,source=/proc/1/ns/,target=/rootns my/image
  1. Inside the container, use nsenter to bring some processes back to the root net namespace
nsenter --net=/rootns/net my/bluetooth/process args

or alternatively, LD_PRELOAD the following shared object before executing bluetooth processes inside the container

#define _GNU_SOURCE

#include <errno.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <sched.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int netns_fd;

__attribute__((constructor))
static void netns_enter() {
	int ret;
	const char *message;
	char *netns_path = getenv("NETNS_ENTER");
	if (!netns_path) {
		message = "NETNS_ENTER is not set\n";
		goto error;
	}
	netns_fd = open(netns_path, O_RDONLY);
	if (netns_fd < 0) {
		message = strerror(errno);
		goto error;
	}
	ret = setns(netns_fd, CLONE_NEWNET);
	return;
error:
	write(1, message, strlen(message));
}

__attribute__((destructor))
static void netns_leave() {
	close(netns_fd);
}
gcc -fPIC -shared -Wl,-soname,netns_enter.so netns_enter.c -o netns_enter.so
LD_PRELOAD=$(pwd)/netns_enter.so NETNS_ENTER=/rootns/net /bluetooth/process/path args

This way you can still use docker port binding for non bluetooth processes inside the container. Pitfall : if the bluetooth process tries to bind to some port it would do so in the host net namespace (as if --net=host was used to start the container).

Hi there,

I needed to get Bluetooth LE working inside a container and I managed to get it up and running with the following container opts: docker run -ti --privileged --net=host debian:jessie --name bletest /bin/bash

Then a small example:

# apt-get update && apt-get install -y bluez bluetooth usbutils
[...]
# service dbus start
[ ok ] Starting system message bus: dbus.
# service dbus status
[ ok ] dbus is running.
# hciconfig hci0
hci0:   Type: BR/EDR  Bus: USB
        BD Address: 00:1A:DE:AD:BE:EF  ACL MTU: 310:10  SCO MTU: 64:8
        DOWN
        RX bytes:0 acl:0 sco:0 events:0 errors:0
        TX bytes:0 acl:0 sco:0 commands:0 errors:0
# hciconfig hci0 up
# hciconfig hci0
hci0:   Type: BR/EDR  Bus: USB
        BD Address: 00:1A:DE:AD:BE:EF  ACL MTU: 310:10  SCO MTU: 64:8
        UP RUNNING PSCAN
        RX bytes:0 acl:0 sco:0 events:0 errors:0
        TX bytes:0 acl:0 sco:0 commands:0 errors:0
# hcitool lescan
LE Scan ...
BC:6A:DE:AD:BE:EF (unknown)
BC:6A:DE:AD:BE:EF LeDevice
BC:6A:DE:AD:BE:EF (unknown)
BC:6A:DE:AD:BE:EF LeDevice
BC:6A:DE:AD:BE:EF (unknown)
^C
# bluetoothd -dn
bluetoothd[209]: Bluetooth daemon 5.23
bluetoothd[209]: src/main.c:parse_config() parsing main.conf
bluetoothd[209]: src/main.c:parse_config() Key file does not have key 'DiscoverableTimeout'
bluetoothd[209]: src/main.c:parse_config() Key file does not have key 'PairableTimeout'
bluetoothd[209]: src/main.c:parse_config() Key file does not have key 'AutoConnectTimeout'
bluetoothd[209]: src/main.c:parse_config() Key file does not have key 'Name'
bluetoothd[209]: src/main.c:parse_config() Key file does not have key 'Class'
bluetoothd[209]: src/main.c:parse_config() Key file does not have key 'DeviceID'
bluetoothd[209]: src/main.c:parse_config() Key file does not have key 'ReverseServiceDiscovery'
bluetoothd[209]: src/gatt.c:gatt_init() Starting GATT server
bluetoothd[209]: src/adapter.c:adapter_init() sending read version command
bluetoothd[209]: Starting SDP server
bluetoothd[209]: src/sdpd-service.c:register_device_id() Adding device id record for 00......
[...]

And here bluetoothd seems to be happy…

I am trying to figure out why the --net=host is needed here. I could understand that the container need --privileged rights, but not to be connected to the host network…

YES!!! Thanks!!! Confirmed this --net=host configuration also works with the armv7/armhf-ubuntu:trusty version as well. I can replicate this.

On a side note, following @jfrazelle thoughts that privileged might not be required, I also tested without this option, and I also got it running.

Summary: both the following let BLE work on a rpi2, with stock armhf Ubuntu 14.04 & docker 1.6.2: docker run -it --net=host --name bletest armv7/armhf-ubuntu bash docker run -it --privileged --net=host --name bletest armv7/armhf-ubuntu bash

@herver MANY THANKS

@TeoTN I was able to get this working inside docker swarm, in a service… docker service create --name mi_atc_reader --cap-add NET_ADMIN --network host mi_atc_reader (refer to https://github.com/AdysTech/mi_atc_reader)

I’ve found out the hard way that --net host or rather network_mode: host doesn’t work with Docker Swarm. Is there any way to access Bluetooth from within docker swarm containers?

Hey guys, I created a bluetooth docker image. I can get bluetooth to work inside of python3 with bluez.

https://hub.docker.com/r/don41382/rpi-python3-with-bluetooth/

Cheers Felix

@marcoh00 , can you also look at bluepy, specifically at https://github.com/IanHarvey/bluepy from @ianharvey and bluepy-helper https://github.com/IanHarvey/bluepy/blob/master/bluepy/bluepy-helper.c which is a little C program that interfaces with Bluetooth? There might be a reasonable way to proxy that through to a container.