Ubuntu Core: Bluetooth support?

I have a 12 node microk8s cluster running on Zimaboard 832’s with Ubuntu Core and I’m loving it. Now I need to pass a bluetooth dongle to one of my deployments and I’m pulling my hair out. Before I even get to this point I need to figure out what dev the dongle is. Here’s what I’ve done so far

  1. Plugged in dongle
  2. dmesg reports a missing drive
  3. Installed the bluez snap
  4. Reboot
  5. dmesg reports fine with the following output

dmesg |egrep -i “wifi|blue|fw|firm”

[    0.448823] Spectre V2 : Enabling Restricted Speculation for firmware calls
[    2.761377] usb 1-2: Product: Bluetooth Radio
[    2.940519] i915 0000:00:02.0: [drm] Finished loading DMC firmware i915/bxt_dmc_ver1_07.bin (v1.7)
[   10.015782] audit: type=1400 audit(1674416859.575:9): apparmor="STATUS" operation="profile_load" profile="unconfined" name="snap-update-ns.bluez" pid=660 comm="apparmor_parser"
[   10.500990] Bluetooth: Core ver 2.22
[   10.501112] NET: Registered PF_BLUETOOTH protocol family
[   10.501115] Bluetooth: HCI device and connection manager initialized
[   10.503097] Bluetooth: HCI socket layer initialized
[   10.503111] Bluetooth: L2CAP socket layer initialized
[   10.503129] Bluetooth: SCO socket layer initialized
[   10.735465] Bluetooth: hci0: RTL: examining hci_ver=0a hci_rev=000b lmp_ver=0a lmp_subver=8761
[   10.738315] Bluetooth: hci0: RTL: rom_version status=0 version=1
[   10.738332] Bluetooth: hci0: RTL: loading rtl_bt/rtl8761bu_fw.bin
[   10.739969] bluetooth hci0: Direct firmware load for rtl_bt/rtl8761bu_fw.bin failed with error -2
[   10.739988] Bluetooth: hci0: RTL: firmware file rtl_bt/rtl8761bu_fw.bin not found
[   33.488408] Bluetooth: BNEP (Ethernet Emulation) ver 1.3
[   33.488420] Bluetooth: BNEP filters: protocol multicast
[   33.488430] Bluetooth: BNEP socket layer initialized
[  114.074617] audit: type=1400 audit(1674416963.465:118): apparmor="DENIED" operation="capable" profile="snap.bluez.bluetoothctl" pid=5131 comm="client-wrapper" capability=2  capname="dac_read_search"
[  114.074637] audit: type=1400 audit(1674416963.465:119): apparmor="DENIED" operation="capable" profile="snap.bluez.bluetoothctl" pid=5131 comm="client-wrapper" capability=1  capname="dac_override"
[  158.647128] audit: type=1400 audit(1674417008.038:122): apparmor="STATUS" operation="profile_replace" info="same as current profile, skipping" profile="unconfined" name="snap-update-ns.bluez" pid=5932 comm="apparmor_parser"
[  159.364487] audit: type=1400 audit(1674417008.758:126): apparmor="STATUS" operation="profile_replace" info="same as current profile, skipping" profile="unconfined" name="snap.bluez.bluetoothctl" pid=5936 comm="apparmor_parser"
[  565.296376] audit: type=1400 audit(1674417414.689:234): apparmor="DENIED" operation="create" profile="snap.bluez.hciconfig" pid=11605 comm="hciconfig" family="bluetooth" sock_type="raw" protocol=1 requested_mask="create" denied_mask="create"
[  573.866432] audit: type=1400 audit(1674417423.261:235): apparmor="DENIED" operation="create" profile="snap.bluez.hciconfig" pid=11666 comm="hciconfig" family="bluetooth" sock_type="raw" protocol=1 requested_mask="create" denied_mask="create"
[  591.025951] audit: type=1400 audit(1674417440.421:236): apparmor="DENIED" operation="create" profile="snap.bluez.hcidump" pid=11898 comm="hcidump" family="bluetooth" sock_type="raw" protocol=1 requested_mask="create" denied_mask="create"
[  595.068067] audit: type=1400 audit(1674417444.461:237): apparmor="DENIED" operation="create" profile="snap.bluez.hcidump" pid=11960 comm="hcidump" family="bluetooth" sock_type="raw" protocol=1 requested_mask="create" denied_mask="create

My assumption from the output above is that I need to do something in apparmor to allow the system to use the device? I don’t know too much about apparmor, honestly. Do I need to create a new bluetooth profile or modify the existing one?

Side note - is there a snap for lsusb?

Can you please post the output from

snap connections bluez
$ sudo snap connections bluez
Interface          Plug                     Slot           Notes
bluetooth-control  bluez:bluetooth-control  -              -
bluez              bluez:client             bluez:service  -
home               bluez:home               -              -
kernel-crypto-api  bluez:kernel-crypto-api  -              -
network-control    bluez:network-control    -              -
uhid               bluez:uhid               :uhid          -
uinput             bluez:uinput             -              -

Thanks - everything looks as expected - the bluez interface for snapd appears to allow all use of the bluetooth socket:

const bluezPermanentSlotAppArmor = `
# Description: Allow operating as the bluez service. This gives privileged
# access to the system.

network bluetooth,
...

Just to double check, could you also please post the contents of /var/lib/snapd/apparmor/profiles/snap.bluez.hciconfig`?

Finally, I wonder if perhaps @ogra or @abeato may have any ideas?

This is your issue I think…

@orga I’m not sure. I think that was before I installed the snap. But I don’t recall honestly

@alexmurray contents of snap.bluez.hciconfig are as follows

# vim:syntax=apparmor

#include <tunables/global>

# snapd supports the concept of 'parallel installs' where snaps with the same
# name are differentiated by '_<instance>' such that foo, foo_bar and foo_baz
# may all be installed on the system. To support this, SNAP_NAME is set to the
# name (eg, 'foo') while SNAP_INSTANCE_NAME is set to the instance name (eg
# 'foo_bar'). The profile name and most rules therefore reference
# SNAP_INSTANCE_NAME. In some cases, snapd will adjust the snap's runtime
# environment so the snap doesn't have to be aware of the distinction (eg,
# SNAP, SNAP_DATA and SNAP_COMMON are all bind mounted onto a directory with
# SNAP_NAME so the security policy will allow writing to both locations (since
# they are equivalent).

# This is a snap name without the instance key
@{SNAP_NAME}="bluez"
# This is a snap name with instance key
@{SNAP_INSTANCE_NAME}="bluez"
@{SNAP_INSTANCE_DESKTOP}="bluez"
@{SNAP_COMMAND_NAME}="hciconfig"
@{SNAP_REVISION}="334"
@{PROFILE_DBUS}="snap_2ebluez_2ehciconfig"
@{INSTALL_DIR}="/{,var/lib/snapd/}snap"

profile "snap.bluez.hciconfig" (attach_disconnected,mediate_deleted) {
  #include <abstractions/base>
  #include <abstractions/consoles>
  #include <abstractions/openssl>

  # While in later versions of the base abstraction, include this explicitly
  # for series 16 and cross-distro
  /etc/ld.so.preload r,

  # The base abstraction doesn't yet have this
  /etc/sysconfig/clock r,
  owner @{PROC}/@{pid}/maps k,
  # While the base abstraction has rules for encryptfs encrypted home and
  # private directories, it is missing rules for directory read on the toplevel
  # directory of the mount (LP: #1848919)
  owner @{HOME}/.Private/ r,
  owner @{HOMEDIRS}/.ecryptfs/*/.Private/ r,

  # for python apps/services
  #include <abstractions/python>
  /etc/python3.[0-9]*/**                                r,

  # explicitly deny noisy denials to read-only filesystems (see LP: #1496895
  # for details)
  deny /usr/lib/python3*/{,**/}__pycache__/ w,
  deny /usr/lib/python3*/{,**/}__pycache__/**.pyc.[0-9]* w,
  # bind mount used here (see 'parallel installs', above)
  deny @{INSTALL_DIR}/{@{SNAP_NAME},@{SNAP_INSTANCE_NAME}}/**/__pycache__/             w,
  deny @{INSTALL_DIR}/{@{SNAP_NAME},@{SNAP_INSTANCE_NAME}}/**/__pycache__/*.pyc.[0-9]* w,

  # for perl apps/services
  #include <abstractions/perl>
  # Missing from perl abstraction
  /usr/lib/@{multiarch}/perl{,5,-base}/auto/**.so* mr,

  # Note: the following dangerous accesses should not be allowed in most
  # policy, but we cannot explicitly deny since other trusted interfaces might
  # add them.
  # Explicitly deny ptrace for now since it can be abused to break out of the
  # seccomp sandbox. https://lkml.org/lkml/2015/3/18/823
  #audit deny ptrace (trace),

  # Explicitly deny capability mknod so apps can't create devices
  #audit deny capability mknod,

  # Explicitly deny mount, remount and umount so apps can't modify things in
  # their namespace
  #audit deny mount,
  #audit deny remount,
  #audit deny umount,

  # End dangerous accesses

  # Note: this potentially allows snaps to DoS other snaps via resource
  # exhaustion but we can't sensibly mediate this today. In the future we may
  # employ cgroup limits, AppArmor rlimit mlock rules or something else.
  capability ipc_lock,

  # for bash 'binaries' (do *not* use abstractions/bash)
  # user-specific bash files
  /etc/bash.bashrc r,
  /etc/inputrc r,
  /etc/environment r,
  /etc/profile r,

  # user/group/seat lookups
  /etc/{passwd,group,nsswitch.conf} r,  # very common
  /var/lib/extrausers/{passwd,group} r,
  /run/systemd/users/[0-9]* r,
  /etc/default/nss r,

  # libnss-systemd (subset from nameservice abstraction)
  #
  #   https://systemd.io/USER_GROUP_API/
  #   https://systemd.io/USER_RECORD/
  #   https://www.freedesktop.org/software/systemd/man/nss-systemd.html
  #
  # Allow User/Group lookups via common VarLink socket APIs. Applications need
  # to either consult all of them or the io.systemd.Multiplexer frontend.
  /run/systemd/userdb/ r,
  /run/systemd/userdb/io.systemd.Multiplexer rw,
  /run/systemd/userdb/io.systemd.DynamicUser rw,        # systemd-exec users
  /run/systemd/userdb/io.systemd.Home rw,               # systemd-home dirs
  /run/systemd/userdb/io.systemd.NameServiceSwitch rw,  # UNIX/glibc NSS
  /run/systemd/userdb/io.systemd.Machine rw,            # systemd-machined

  /etc/libnl-3/{classid,pktloc} r,      # apps that use libnl

  # For snappy reexec on 4.8+ kernels
  /usr/lib/snapd/snap-exec m,

  # For gdb support
  /usr/lib/snapd/snap-gdb-shim ixr,
  /usr/lib/snapd/snap-gdbserver-shim ixr,

  # For in-snap tab completion
  /etc/bash_completion.d/{,*} r,
  /usr/lib/snapd/etelpmoc.sh ixr,               # marshaller (see complete.sh for out-of-snap unmarshal)
  /usr/share/bash-completion/bash_completion r, # user-provided completions (run in-snap) may use functions from here

  # uptime
  @{PROC}/uptime r,
  @{PROC}/loadavg r,

  # Allow reading /etc/os-release. On Ubuntu 16.04+ it is a symlink to /usr/lib
  # which is allowed by the base abstraction, but on 14.04 it is an actual file
  # so need to add it here. Also allow read locks on the file.
  /etc/os-release rk,
  /usr/lib/os-release k,

  # systemd native journal API (see sd_journal_print(4)). This should be in
  # AppArmor's base abstraction, but until it is, include here. We include
  # the base journal path as well as the journal namespace pattern path. Each
  # journal namespace for quota groups will be prefixed with 'snap-'.
  /run/systemd/journal{,.snap-*}/socket w,
  /run/systemd/journal{,.snap-*}/stdout rw, # 'r' shouldn't be needed, but journald
                                            # doesn't leak anything so allow

  # snapctl and its requirements
  /usr/bin/snapctl ixr,
  /usr/lib/snapd/snapctl ixr,
  @{PROC}/sys/net/core/somaxconn r,
  /run/snapd-snap.socket rw,

  # Note: for now, don't explicitly deny this noisy denial so --devmode isn't
  # broken but eventually we may conditionally deny this since it is an
  # information leak.
  #deny /{,var/}run/utmp r,

  # java
  @{PROC}/@{pid}/ r,
  @{PROC}/@{pid}/fd/ r,
  owner @{PROC}/@{pid}/auxv r,
  @{PROC}/sys/vm/zone_reclaim_mode r,
  /etc/lsb-release r,
  /sys/devices/**/read_ahead_kb r,
  /sys/devices/system/cpu/** r,
  /sys/devices/system/node/node[0-9]*/* r,
  /sys/kernel/mm/transparent_hugepage/enabled r,
  /sys/kernel/mm/transparent_hugepage/defrag r,
  # NOTE: this leaks running process but java seems to want it (even though it
  # seems to operate ok without it) and SDL apps crash without it. Allow owner
  # match until AppArmor kernel var is available to solve this properly (see
  # LP: #1546825 for details). comm is a subset of cmdline, so allow it too.
  owner @{PROC}/@{pid}/cmdline r,
  owner @{PROC}/@{pid}/comm r,

  # Per man(5) proc, the kernel enforces that a thread may only modify its comm
  # value or those in its thread group.
  owner @{PROC}/@{pid}/task/@{tid}/comm rw,

  # Allow reading and writing to our file descriptors in /proc which, for
  # example, allow access to /dev/std{in,out,err} which are all symlinks to
  # /proc/self/fd/{0,1,2} respectively. To support the open(..., O_TMPFILE)
  # linkat() temporary file technique, allow all fds. Importantly, access to
  # another task's fd via this proc interface is mediated via 'ptrace (read)'
  # (readonly) and 'ptrace (trace)' (read/write) which is denied by default, so
  # this rule by itself doesn't allow opening another snap's fds via proc.
  owner @{PROC}/@{pid}/{,task/@{tid}}fd/[0-9]* rw,

  # Miscellaneous accesses
  /dev/{,u}random w,
  /etc/machine-id r,
  /etc/mime.types r,
  @{PROC}/ r,
  @{PROC}/version r,
  @{PROC}/version_signature r,
  /etc/{,writable/}hostname r,
  /etc/{,writable/}localtime r,
  /etc/{,writable/}mailname r,
  /etc/{,writable/}timezone r,
  owner @{PROC}/@{pid}/cgroup rk,
  @{PROC}/@{pid}/io r,
  owner @{PROC}/@{pid}/limits r,
  owner @{PROC}/@{pid}/loginuid r,
  @{PROC}/@{pid}/smaps r,
  @{PROC}/@{pid}/stat r,
  @{PROC}/@{pid}/statm r,
  @{PROC}/@{pid}/status r,
  @{PROC}/@{pid}/task/ r,
  @{PROC}/@{pid}/task/[0-9]*/smaps r,
  @{PROC}/@{pid}/task/[0-9]*/stat r,
  @{PROC}/@{pid}/task/[0-9]*/statm r,
  @{PROC}/@{pid}/task/[0-9]*/status r,
  @{PROC}/sys/fs/pipe-max-size r,
  @{PROC}/sys/kernel/hostname r,
  @{PROC}/sys/kernel/osrelease r,
  @{PROC}/sys/kernel/ostype r,
  @{PROC}/sys/kernel/pid_max r,
  @{PROC}/sys/kernel/yama/ptrace_scope r,
  @{PROC}/sys/kernel/shmmax r,
  # Allow apps to introspect the level of dbus mediation AppArmor implements.
  /sys/kernel/security/apparmor/features/dbus/mask r,
  @{PROC}/sys/fs/file-max r,
  @{PROC}/sys/fs/file-nr r,
  @{PROC}/sys/fs/inotify/max_* r,
  @{PROC}/sys/kernel/pid_max r,
  @{PROC}/sys/kernel/random/boot_id r,
  @{PROC}/sys/kernel/random/entropy_avail r,
  @{PROC}/sys/kernel/random/uuid r,
  @{PROC}/sys/kernel/cap_last_cap r,
  # Allow access to the uuidd daemon (this daemon is a thin wrapper around
  # time and getrandom()/{,u}random and, when available, runs under an
  # unprivilged, dedicated user).
  /run/uuidd/request rw,
  /sys/devices/virtual/tty/{console,tty*}/active r,
  /sys/fs/cgroup/memory/{,user.slice/}memory.limit_in_bytes r,
  /sys/fs/cgroup/memory/{,**/}snap.@{SNAP_INSTANCE_NAME}{,.*}/memory.limit_in_bytes r,
  /sys/fs/cgroup/memory/{,**/}snap.@{SNAP_INSTANCE_NAME}{,.*}/memory.stat r,
  /sys/fs/cgroup/cpu,cpuacct/{,user.slice/}cpu.cfs_{period,quota}_us r,
  /sys/fs/cgroup/cpu,cpuacct/{,**/}snap.@{SNAP_INSTANCE_NAME}{,.*}/cpu.cfs_{period,quota}_us r,
  /sys/fs/cgroup/cpu,cpuacct/{,user.slice/}cpu.shares r,
  /sys/fs/cgroup/cpu,cpuacct/{,**/}snap.@{SNAP_INSTANCE_NAME}{,.*}/cpu.shares r,
  /sys/kernel/mm/transparent_hugepage/hpage_pmd_size r,
  /sys/module/apparmor/parameters/enabled r,
  /{,usr/}lib/ r,

  # Reads of oom_adj and oom_score_adj are safe
  owner @{PROC}/@{pid}/oom_{,score_}adj r,

  # Note: for now, don't explicitly deny write access so --devmode isn't broken
  # but eventually we may conditionally deny this since it allows the process
  # to increase the oom heuristic of other processes (make them more likely to
  # be killed). Once AppArmor kernel var is available to solve this properly,
  # this can safely be allowed since non-root processes won't be able to
  # decrease the value and root processes will only be able to with
  # 'capability sys_resource,' which we deny be default.
  # deny owner @{PROC}/@{pid}/oom_{,score_}adj w,

  # Eases hardware assignment (doesn't give anything away)
  /etc/udev/udev.conf r,
  /sys/       r,
  /sys/bus/   r,
  /sys/class/ r,

  # this leaks interface names and stats, but not in a way that is traceable
  # to the user/device
  @{PROC}/net/dev r,
  @{PROC}/@{pid}/net/dev r,

  # Read-only of this snap
  /var/lib/snapd/snaps/@{SNAP_NAME}_*.snap r,

  # Read-only of snapd restart state for snapctl specifically
  /var/lib/snapd/maintenance.json r,

  # Read-only for the install directory
  # bind mount used here (see 'parallel installs', above)
  @{INSTALL_DIR}/{@{SNAP_NAME},@{SNAP_INSTANCE_NAME}}/                   r,
  @{INSTALL_DIR}/{@{SNAP_NAME},@{SNAP_INSTANCE_NAME}/@{SNAP_REVISION}}/    r,
  @{INSTALL_DIR}/{@{SNAP_NAME},@{SNAP_INSTANCE_NAME}/@{SNAP_REVISION}}/**  mrklix,

  # Read-only install directory for other revisions to help with bugs like
  # LP: #1616650 and LP: #1655992
  @{INSTALL_DIR}/{@{SNAP_NAME},@{SNAP_INSTANCE_NAME}}/**  mrkix,

  # Read-only home area for other versions
  # bind mount *not* used here (see 'parallel installs', above)
  owner @{HOME}/snap/@{SNAP_INSTANCE_NAME}/                  r,
  owner @{HOME}/snap/@{SNAP_INSTANCE_NAME}/**                mrkix,

  # Experimental snap folder changes
  owner @{HOME}/.snap/data/@{SNAP_INSTANCE_NAME}/                    r,
  owner @{HOME}/.snap/data/@{SNAP_INSTANCE_NAME}/**                  mrkix,
  owner @{HOME}/.snap/data/@{SNAP_INSTANCE_NAME}/@{SNAP_REVISION}/** wl,
  owner @{HOME}/.snap/data/@{SNAP_INSTANCE_NAME}/common/**           wl,

  owner @{HOME}/Snap/@{SNAP_INSTANCE_NAME}/                          r,
  owner @{HOME}/Snap/@{SNAP_INSTANCE_NAME}/**                        mrkixwl,

  # Writable home area for this version.
  # bind mount *not* used here (see 'parallel installs', above)
  owner @{HOME}/snap/@{SNAP_INSTANCE_NAME}/@{SNAP_REVISION}/** wl,
  owner @{HOME}/snap/@{SNAP_INSTANCE_NAME}/common/** wl,

  # Read-only system area for other versions
  # bind mount used here (see 'parallel installs', above)
  /var/snap/{@{SNAP_NAME},@{SNAP_INSTANCE_NAME}}/   r,
  owner @{HOME}/Snap/@{SNAP_INSTANCE_NAME}/**                        mrkixwl,

  # Writable home area for this version.
  # bind mount *not* used here (see 'parallel installs', above)
  owner @{HOME}/snap/@{SNAP_INSTANCE_NAME}/@{SNAP_REVISION}/** wl,
  owner @{HOME}/snap/@{SNAP_INSTANCE_NAME}/common/** wl,

  # Read-only system area for other versions
  # bind mount used here (see 'parallel installs', above)
  /var/snap/{@{SNAP_NAME},@{SNAP_INSTANCE_NAME}}/   r,
  /var/snap/{@{SNAP_NAME},@{SNAP_INSTANCE_NAME}}/** mrkix,

  # Writable system area only for this version
  # bind mount used here (see 'parallel installs', above)
  /var/snap/{@{SNAP_NAME},@{SNAP_INSTANCE_NAME}}/@{SNAP_REVISION}/** wl,
  /var/snap/{@{SNAP_NAME},@{SNAP_INSTANCE_NAME}}/common/** wl,

  # The ubuntu-core-launcher creates an app-specific private restricted /tmp
  # and will fail to launch the app if something goes wrong. As such, we can
  # simply allow full access to /tmp.
  /tmp/   r,
  /tmp/** mrwlkix,

  # App-specific access to files and directories in /dev/shm. We allow file
  # access in /dev/shm for shm_open() and files in subdirectories for open()
  # bind mount *not* used here (see 'parallel installs', above)
  /{dev,run}/shm/snap.@{SNAP_INSTANCE_NAME}.** mrwlkix,
  # Also allow app-specific access for sem_open()
  /{dev,run}/shm/sem.snap.@{SNAP_INSTANCE_NAME}.* mrwlk,

  # Snap-specific XDG_RUNTIME_DIR that is based on the UID of the user
  # bind mount *not* used here (see 'parallel installs', above)
  owner /run/user/[0-9]*/snap.@{SNAP_INSTANCE_NAME}/   rw,
  owner /run/user/[0-9]*/snap.@{SNAP_INSTANCE_NAME}/** mrwklix,

  # Allow apps from the same package to communicate with each other via an
  # abstract or anonymous socket
  unix (bind, listen) addr="@snap.@{SNAP_INSTANCE_NAME}.**",
  unix peer=(label=snap.@{SNAP_INSTANCE_NAME}.*),

  # Allow apps from the same package to communicate with each other via DBus.
  # Note: this does not grant access to the DBus sockets of well known buses
  # (will still need to use an appropriate interface for that).
  dbus (receive, send) peer=(label=snap.@{SNAP_INSTANCE_NAME}.*),
  # In addition to the above, dbus-run-session attempts reading these files
  # from the snap base runtime.
  /usr/share/dbus-1/services/{,*} r,
  /usr/share/dbus-1/system-services/{,*} r,
  # Allow apps to perform DBus introspection on org.freedesktop.DBus for both
  # the system and session buses.
  # Note: this does not grant access to the DBus sockets of these buses, but
  # we grant it here since it is missing from the dbus abstractions
  # (LP: #1866168)
  dbus (send)
      bus={session,system}
      path=/org/freedesktop/DBus
      interface=org.freedesktop.DBus.Introspectable
      member=Introspect
      peer=(label=unconfined),

  # Allow apps from the same package to signal each other via signals
  signal peer=snap.@{SNAP_INSTANCE_NAME}.*,

  # Allow receiving signals from all snaps (and focus on mediating sending of
  # signals)
  signal (receive) peer=snap.*,

  # Allow receiving signals from unconfined (eg, systemd)
  signal (receive) peer=unconfined,

  # for 'udevadm trigger --verbose --dry-run --tag-match=snappy-assign'
  /{,usr/}{,s}bin/udevadm ixr,
  /etc/udev/udev.conf r,
  /{,var/}run/udev/tags/snappy-assign/ r,
  @{PROC}/cmdline r,
  /sys/devices/**/uevent r,

  # LP: #1447237: adding '--property-match=SNAPPY_APP=<pkgname>' to the above
  # requires:
  #   /run/udev/data/* r,
  # but that reveals too much about the system and cannot be granted to apps
  # by default at this time.

  # For convenience, allow apps to see what is in /dev even though cgroups
  # will block most access
  /dev/ r,
  /dev/**/ r,

  # Allow setting up pseudoterminal via /dev/pts system. This is safe because
  # the launcher uses a per-app devpts newinstance.
  /dev/ptmx rw,

  # Do the same with /sys/devices and /sys/class to help people using hw-assign
  /sys/devices/ r,
  /sys/devices/**/ r,
  /sys/class/ r,
  /sys/class/**/ r,

  # Allow all snaps to chroot
  capability sys_chroot,

  # Lttng tracing is very noisy and should not be allowed by confined apps. Can
  # safely deny for the normal case (LP: #1260491). If/when an lttng-trace
  # interface is needed, we can rework this.
  deny /{dev,run,var/run}/shm/lttng-ust-* rw,

  # Allow read-access on /home/ for navigating to other parts of the
  # filesystem. While this allows enumerating users, this is already allowed
  # via /etc/passwd and getent.
  @{HOMEDIRS}/ r,

  # Allow read-access to / for navigating to other parts of the filesystem.
  / r,

  # Snap-specific run directory. Bind mount *not* used here
  # (see 'parallel installs', above)
  /run/snap.@{SNAP_INSTANCE_NAME}/ rw,
  /run/snap.@{SNAP_INSTANCE_NAME}/** mrwklix,

  # Snap-specific lock directory and prerequisite navigation permissions.
  /run/lock/ r,
  /run/lock/snap.@{SNAP_INSTANCE_NAME}/ rw,
  /run/lock/snap.@{SNAP_INSTANCE_NAME}/** mrwklix,



  # Default rules for core base runtimes

  # The base abstraction doesn't yet have this
  /{,usr/}lib/terminfo/** rk,
  /usr/share/terminfo/** k,
  /usr/share/zoneinfo/** k,

  # for python apps/services
  /usr/bin/python{,2,2.[0-9]*,3,3.[0-9]*} ixr,
  # additional accesses needed for newer pythons in later bases
  /usr/lib{,32,64}/python3.[0-9]*/**.{pyc,so}           mr,
  /usr/lib{,32,64}/python3.[0-9]*/**.{egg,py,pth}       r,
  /usr/lib{,32,64}/python3.[0-9]*/{site,dist}-packages/ r,
  /usr/lib{,32,64}/python3.[0-9]*/lib-dynload/*.so      mr,
  /usr/include/python3.[0-9]*/pyconfig.h               r,

  # for perl apps/services
  /usr/bin/perl{,5*} ixr,
  # AppArmor <2.12 doesn't have rules for perl-base, so add them here
  /usr/lib/@{multiarch}/perl{,5,-base}/**            r,
  /usr/lib/@{multiarch}/perl{,5,-base}/[0-9]*/**.so* mr,

  # for bash 'binaries' (do *not* use abstractions/bash)
  # user-specific bash files
  /{,usr/}bin/bash ixr,
  /{,usr/}bin/dash ixr,
  /usr/share/terminfo/** r,

  # Common utilities for shell scripts
  /{,usr/}bin/arch ixr,
  /{,usr/}bin/{,g,m}awk ixr,
  /{,usr/}bin/base32 ixr,
  /{,usr/}bin/base64 ixr,
  /{,usr/}bin/basename ixr,
  /{,usr/}bin/bunzip2 ixr,
  /{,usr/}bin/busctl ixr,
  /{,usr/}bin/bzcat ixr,
  /{,usr/}bin/bzdiff ixr,
  /{,usr/}bin/bzgrep ixr,
  /{,usr/}bin/bzip2 ixr,
  /{,usr/}bin/cat ixr,
  /{,usr/}bin/chgrp ixr,
  /{,usr/}bin/chmod ixr,
  /{,usr/}bin/chown ixr,
  /{,usr/}bin/clear ixr,
  /{,usr/}bin/cmp ixr,
  /{,usr/}bin/cp ixr,
  /{,usr/}bin/cpio ixr,
  /{,usr/}bin/cut ixr,
  /{,usr/}bin/date ixr,
  /{,usr/}bin/dbus-daemon ixr,
  /{,usr/}bin/dbus-run-session ixr,
  /{,usr/}bin/dbus-send ixr,
  /{,usr/}bin/dd ixr,
  /{,usr/}bin/diff{,3} ixr,
  /{,usr/}bin/dir ixr,
  /{,usr/}bin/dirname ixr,
  /{,usr/}bin/du ixr,
  /{,usr/}bin/echo ixr,
  /{,usr/}bin/{,e,f,r}grep ixr,
  /{,usr/}bin/env ixr,
  /{,usr/}bin/expr ixr,
  /{,usr/}bin/false ixr,
  /{,usr/}bin/find ixr,
  /{,usr/}bin/flock ixr,
  /{,usr/}bin/fmt ixr,
  /{,usr/}bin/fold ixr,
  /{,usr/}bin/getconf ixr,
  /{,usr/}bin/getent ixr,
  /{,usr/}bin/getopt ixr,
  /{,usr/}bin/groups ixr,
  /{,usr/}bin/gzip ixr,
  /{,usr/}bin/head ixr,
  /{,usr/}bin/hostname ixr,
  /{,usr/}bin/id ixr,
  /{,usr/}bin/igawk ixr,
  /{,usr/}bin/infocmp ixr,
  /{,usr/}bin/kill ixr,
  /{,usr/}bin/ldd ixr,
  /{usr/,}lib{,32,64}/ld{,32,64}-*.so ix,
  /{usr/,}lib/@{multiarch}/ld{,32,64}-*.so* ix,
  /{,usr/}bin/less{,file,pipe} ixr,
  /{,usr/}bin/ln ixr,
  /{,usr/}bin/line ixr,
  /{,usr/}bin/link ixr,
  /{,usr/}bin/locale ixr,
  /{,usr/}bin/logger ixr,
  /{,usr/}bin/ls ixr,
  /{,usr/}bin/md5sum ixr,
  /{,usr/}bin/mkdir ixr,
  /{,usr/}bin/mkfifo ixr,
  /{,usr/}bin/mknod ixr,
  /{,usr/}bin/mktemp ixr,
  /{,usr/}bin/more ixr,
  /{,usr/}bin/mv ixr,
  /{,usr/}bin/nice ixr,
  /{,usr/}bin/nohup ixr,
  /{,usr/}bin/numfmt ixr,
  /{,usr/}bin/od ixr,
  /{,usr/}bin/openssl ixr, # may cause harmless capability block_suspend denial
  /{,usr/}bin/paste ixr,
  /{,usr/}bin/pgrep ixr,
  /{,usr/}bin/printenv ixr,
  /{,usr/}bin/printf ixr,
  /{,usr/}bin/ps ixr,
  /{,usr/}bin/pwd ixr,
  /{,usr/}bin/readlink ixr,
  /{,usr/}bin/realpath ixr,
  /{,usr/}bin/rev ixr,
  /{,usr/}bin/rm ixr,
  /{,usr/}bin/rmdir ixr,
  /{,usr/}bin/run-parts ixr,
  /{,usr/}bin/sed ixr,
  /{,usr/}bin/seq ixr,
  /{,usr/}bin/sha{1,224,256,384,512}sum ixr,
  /{,usr/}bin/shuf ixr,
  /{,usr/}bin/sleep ixr,
  /{,usr/}bin/sort ixr,
  /{,usr/}bin/stat ixr,
  /{,usr/}bin/stdbuf ixr,
  /{,usr/}bin/stty ixr,
  /{,usr/}bin/sync ixr,
  /{,usr/}bin/systemd-cat ixr,
  /{,usr/}bin/tac ixr,
  /{,usr/}bin/tail ixr,
  /{,usr/}bin/tar ixr,
  /{,usr/}bin/tee ixr,
  /{,usr/}bin/test ixr,
  /{,usr/}bin/tempfile ixr,
  /{,usr/}bin/tset ixr,
  /{,usr/}bin/touch ixr,
  /{,usr/}bin/tput ixr,
  /{,usr/}bin/tr ixr,
  /{,usr/}bin/true ixr,
  /{,usr/}bin/tty ixr,
  /{,usr/}bin/uname ixr,
  /{,usr/}bin/uniq ixr,
  /{,usr/}bin/unlink ixr,
  /{,usr/}bin/unxz ixr,
  /{,usr/}bin/unzip ixr,
  /{,usr/}bin/uptime ixr,
  /{,usr/}bin/vdir ixr,
  /{,usr/}bin/wc ixr,
  /{,usr/}bin/which{,.debianutils} ixr,
  /{,usr/}bin/xargs ixr,
  /{,usr/}bin/xz ixr,
  /{,usr/}bin/yes ixr,
  /{,usr/}bin/zcat ixr,
  /{,usr/}bin/z{,e,f}grep ixr,
  /{,usr/}bin/zip ixr,
  /{,usr/}bin/zipgrep ixr,

  # lsb-release
  /usr/bin/lsb_release ixr,
  /usr/bin/ r,
  /usr/share/distro-info/*.csv r,

  # For printing the cache (we don't allow updating the cache)
  /{,usr/}sbin/ldconfig{,.real} ixr,

  # Allow all snaps to chroot
  /{,usr/}sbin/chroot ixr,

# Layout path: /usr/etc/bluetooth
# (no extra permissions required for symlink)
# Layout path: /usr/var/lib/bluetooth
# (no extra permissions required for symlink)
# Allow each snaps to access each their own folder on the
# ubuntu-save partition, with write permissions.
/var/lib/snapd/save/snap/@{SNAP_INSTANCE_NAME}/ rw,
/var/lib/snapd/save/snap/@{SNAP_INSTANCE_NAME}/** mrwklix,

}

Ah, I cleared the dmesg buffer and rebooted, it seems your right

[    9.698519] audit: type=1400 audit(1674437447.251:9): apparmor="STATUS" operation="profile_load" profile="unconfined" name="snap-update-ns.bluez" pid=669 comm="apparmor_parser"
[   10.095214] bluetooth hci0: Direct firmware load for rtl_bt/rtl8761bu_fw.bin failed with error -2

I saw a post somewhere about symlinking existing firmware but that part of the fs seems to be read-only. Do you know of a proper solution for this?

I’m still surprised that these AppArmor denials are occurring - looking at the AppArmor profile I can’t see the expected line I quoted above so at least that is consistent (ie. the AppArmor profile doesn’t contain the necessary rule and hence the socket creation is denied) - but let’s get the firmware issue sorted first and then can come back to the AppArmor issue after it it is still seeming to cause issues.

I’m super new to Ubuntu Core. Been using Ubuntu for years but this is a whole new thing to wrap my head around, e.g the read only file system. You don’t happen to have a link to any docs that show how to load the firmware, do you?

There is not really a “solution” since the kernel snap is still lacking interfaces to add external firmware or external modules to it… there is an ugly hack you can apply on UC devices that have a user:

copy the whole of /lib/firmware into ~/firmware and create a systemd service that bind mounts it over the existing /lib/firmware like:

[Unit]
Description=Bind mount /lib/firmware
Before=systemd-networkd.service

[Service]
Type=oneshot
ExecStart=/usr/bin/mount --bind /home/ogra/firmware /lib/firmware

[Install]
WantedBy=multi-user.target

now you can add additional firmware files into ~/firmware as needed … (…but will indeed have to care yourself for it to stay up to date, so you should do a regular comparison check of ~/firmware against /snap/pi-kernel/current/firmware/)

Perfect, I’ll give that a try. Do I need to stop and disable any services?

you should reboot after adding and enabling the new service, most firmware is loaded during early boot and what you have in ram will not just be replaced at runtime, so a reboot is best…

Thank you! I think I’m nearly there

From local machine

wget https://raw.githubusercontent.com/Realtek-OpenSource/android_hardware_realtek/rtk1395/bt/rtkbt/Firmware/BT/rtl8761b_config
wget https://raw.githubusercontent.com/Realtek-OpenSource/android_hardware_realtek/rtk1395/bt/rtkbt/Firmware/BT/rtl8761b_fw
scp ./rtl8761b_config user@host:~/
scp ./rtl8761b_fw user@host:~/

From ubuntu core machine

sudo mkdir ~/firmware
sudo cp -r /lib/firmware ~/firmware/
cp ./rtl8761b_config ~/firmware/rtl_bt/
cp ./rtl8761b_fw ~/firmware/rtl_bt/
ln -s /home/user/firmware/rtl_bt/rtl8761b_config /home/user/firmware/rtl_bt/rtl8761bu_config.bin
ln -s /home/user/firmware/rtl_bt/rtl8761b_fw /home/user/firmware/rtl_bt/rtl8761b_fw.bin

$ sudo cat >> /etc/systemd/system/customdrivers.service <<EOL
[Unit]
Description=Bind mount /lib/firmware
Before=systemd-networkd.service

[Service]
Type=oneshot
ExecStart=/usr/bin/mount --bind /home/user/firmware /lib/firmware

[Install]
WantedBy=multi-user.target

EOL

sudo systemctl enable customdrivers
sudo systemctl start customdrivers
sudo dmesg -c
sudo reboot

sudo dmesg
...truncated for brevity...
[   10.396639] EDAC pnd2: Failed to register device with error -22.
[   10.403984] Bluetooth: hci0: RTL: examining hci_ver=0a hci_rev=000b lmp_ver=0a lmp_subver=8761
[   10.404963] Bluetooth: hci0: RTL: rom_version status=0 version=1
[   10.404972] Bluetooth: hci0: RTL: loading rtl_bt/rtl8761bu_fw.bin
[   10.413527] Bluetooth: hci0: RTL: loading rtl_bt/rtl8761bu_config.bin
[   10.413651] bluetooth hci0: Direct firmware load for rtl_bt/rtl8761bu_config.bin failed with error -2
[   10.413673] Bluetooth: hci0: RTL: cfg_sz -2, total sz 11664
[   10.462405] Invalid pltconfig, ensure IPC1 device is enabled in BIOS
[   10.502016] Bluetooth: hci0: RTL: fw version 0x097bec43
[   10.522193] Invalid pltconfig, ensure IPC1 device is enabled in BIOS
[   10.528322] intel_rapl_common: Found RAPL domain package
[   10.528334] intel_rapl_common: Found RAPL domain core
[   10.528338] intel_rapl_common: Found RAPL domain uncore
[   10.528341] intel_rapl_common: Found RAPL domain dram
[   10.574385] Invalid pltconfig, ensure IPC1 device is enabled in BIOS
[   11.901682] Generic FE-GE Realtek PHY r8169-0-300:00: attached PHY driver (mii_bus:phy_addr=r8169-0-300:00, irq=MAC)
[   12.097833] r8169 0000:03:00.0 enp3s0: Link is Down
[   12.125715] Generic FE-GE Realtek PHY r8169-0-200:00: attached PHY driver (mii_bus:phy_addr=r8169-0-200:00, irq=MAC)
[   12.181390] loop19: detected capacity change from 0 to 8
[   12.321699] r8169 0000:02:00.0 enp2s0: Link is Down
[   15.622235] r8169 0000:03:00.0 enp3s0: Link is Up - 1Gbps/Full - flow control rx/tx
[   15.622275] IPv6: ADDRCONF(NETDEV_CHANGE): enp3s0: link becomes ready
[   15.825320] r8169 0000:02:00.0 enp2s0: Link is Up - 1Gbps/Full - flow control rx/tx
[   15.825376] IPv6: ADDRCONF(NETDEV_CHANGE): enp2s0: link becomes ready
[   25.004679] Bluetooth: BNEP (Ethernet Emulation) ver 1.3
[   25.004693] Bluetooth: BNEP filters: protocol multicast
[   25.004705] Bluetooth: BNEP socket layer initialized
[   25.017399] kauditd_printk_skb: 111 callbacks suppressed
[   25.017410] audit: type=1400 audit(1674486889.939:123): apparmor="DENIED" operation="create" profile="snap.bluez.bluez" pid=857 comm="bluetoothd" family="alg" sock_type="seqpacket" protocol=0 requested_mask="create" denied_mask="create"
[   25.018471] audit: type=1400 audit(1674486889.943:124): apparmor="STATUS" operation="profile_load" profile="snap.microk8s.daemon-containerd" name="cri-containerd.apparmor.d" pid=1224 comm="apparmor_parser"
[   25.035929] Bluetooth: RFCOMM TTY layer initialized
[   25.035951] Bluetooth: RFCOMM socket layer initialized
[   25.035968] Bluetooth: RFCOMM ver 1.11
[   27.575698] bpfilter: Loaded bpfilter_umh pid 1283
[   27.576287] Started bpfilter
[   30.534257] cfg80211: Loading compiled-in X.509 certificates for regulatory database
[   30.534710] cfg80211: Loaded X.509 cert 'sforshee: 00b28ddf47aef9cea7'

Hey check that out!

krisclarkdev@k8s-02:~$ sudo bluetoothctl
Agent registered
[CHG] Controller 00:2A:7D:00:40:35 Pairable: yes
[bluetooth]# scan on
Failed to start discovery: org.bluez.Error.NotReady

PROGRESS!

From local machine

wget https://raw.githubusercontent.com/Realtek-OpenSource/android_hardware_realtek/rtk1395/bt/rtkbt/Firmware/BT/rtl8761b_config
wget https://raw.githubusercontent.com/Realtek-OpenSource/android_hardware_realtek/rtk1395/bt/rtkbt/Firmware/BT/rtl8761b_fw
scp ./rtl8761b_config user@host:~/
scp ./rtl8761b_fw user@host:~/

From ubuntu core machine

sudo mkdir ~/firmware
sudo cp -r /lib/firmware ~/firmware/
cp ./rtl8761b_config ~/firmware/rtl_bt/
cp ./rtl8761b_fw ~/firmware/rtl_bt/
ln -s /home/user/firmware/rtl_bt/rtl8761b_config /home/user/firmware/rtl_bt/rtl8761bu_config.bin
ln -s /home/user/firmware/rtl_bt/rtl8761b_fw /home/user/firmware/rtl_bt/rtl8761b_fw.bin

$ sudo cat >> /etc/systemd/system/customdrivers.service <<EOL
[Unit]
Description=Bind mount /lib/firmware
Before=systemd-networkd.service

[Service]
Type=oneshot
ExecStart=/usr/bin/mount --bind /home/user/firmware /lib/firmware

[Install]
WantedBy=multi-user.target

EOL

sudo systemctl enable customdrivers
sudo systemctl start customdrivers
sudo dmesg -c
sudo reboot

sudo bluetoothctl
power on
scan on

try “power on” before “scan on” …

yup yup, just did that :slight_smile: posted the complete solution, thank you both so very much

1 Like

How does a device present itself under /dev in core? I’m looking for /dev/rfcomm0 but don’t see anything

well, this exceeds my knowledge, perhaps @abeato has a hint here, i think newer bluez 5.x does not actually expose devices in /dev anymore (the older one did have various /dev/hci* devices)