Confined snap support for CAN interfaces (rtnetlink)

My question relates to https://snapcraft.io/gse-app-cli

The snap is used as part of our ground support tooling for satellite assembly, integration and testing. The focus on this conversation is on the CAN interface (UART works nicely).

I would like to run this snap without sudo, and have the ability to configure the CAN interface.

The issue is that without sudo, I get a permission issues:

(sofia) flotter@flotter-laptop:~/sofia$ gse-app-cli program adcs --can --pcan can0 --address 1
Checking PCAN driver ...
Checking web server access ...
Full ADCS upgrade to latest control programs
Connecting to device...
Searching for CAN devices:
 - can0
RTNETLINK answers: Operation not permitted

I believe the core issue is that the application does not have CAP_NET_ADMIN, and I cannot find any way to transparently give it that capability without running the snap with sudo.

This would be acceptible for me if the could still access the $SUDO_USER home directory for writing and reading, but I believe the home interface only allows read: all.

Using sudo as the solution here feel wrong since then all the $HOME related directory references moves to root/. The CLI application can take input files and produces log files as output, so this is where the sudo solution becomes messy, since I cannot access the current sudo user’s directory, and also not drop for consume files from the “current” dirctory is that is inside $HOME. It becomes a bad end-user experience in my opinion.

I have looked at the netlink* interfaces, but these only seem to work with core/gadget snaps.

  1. Am I missing something obvious here?
  2. Is there a more elegant solution for this to work without sudo.

The snapcraft.yaml is below:

(sofia) flotter@flotter-laptop:~/sofia$ cat tools/cubesupport/snap/snapcraft.yaml 
name: gse-app-cli
version: '5.0.15.1'
title: Cubespace GSE App CLI
contact: support@cubespace.co.za
website: https://www.cubespace.co.za
license: Proprietary
summary: CLI tool for configuration and management of ADCS nodes.
description: |
  The GSE App provides functionality used for configuration management
  and firmware / bootloader updates of ADCS nodes.

  The GSE App is running as a strictly confined snap, which means that
  by default it does not have any access to external USB peripherals or
  the correct permissions to configure them (even with sudo).

  Note: You still need to run gse-app-cli as sudo, as once the snap
  container grants you access, normal permission requirements still hold.

  Enabling USB-serial support
  ---------------------------

  1. Enable hotplugging support.

  sudo snap set system experimental.hotplug=true

  2. List the available serial devices.

  sudo snap interface serial-port

  name:          serial-port
  summary:       allows accessing a specific serial port
  documentation: https://snapcraft.io/docs/serial-port-interface
  plugs:
    - gse-app-cli
  slots:
    - snapd:ttl232r-3v3 (allows accessing a specific serial port)  

  3. Connect the interface to grant the snap access to your port.

  sudo snap connect gse-app-cli:serial-port core:ttl232r-3v3

  4. Run the command you wish.

  sudo gse-app-cli rombl erase --uart --port /dev/ttyUSB0 

  Enabling CAN support
  --------------------

  1. Enable the network configuration interface for your snap.

  sudo snap connect gse-app-cli:network-control

  2. Enable CAN bus access for your snap.

  sudo snap connect gse-app-cli:can-bus

  3. Run the command you wish.

  sudo gse-app-cli rombl erase --can --pcan can0

  If you run into any issues, please contact support@cubespace.co.za.

base: core24
grade: stable
confinement: strict

lint:
  ignore:
    - metadata
    - library:
      # Unused warning, but needs to be here for dlopen.
      - libbrotlidec.so.1
      - libbrotlienc.so.1
      - libicutest.so.74
      - libicuio.so.74
      - liblttng-ust-ctl.so.5
      - liblttng-ust-cyg-profile-fast.so.1
      - liblttng-ust-cyg-profile.so.1
      - liblttng-ust-dl.so.1
      - liblttng-ust-fd.so.1
      - liblttng-ust-fork.so.1
      - liblttng-ust-libc-wrapper.so.1
      - liblttng-ust-pthread-wrapper.so.1
      - libssl.so.3
      - libunwind-coredump.so.0
      - libunwind-ptrace.so.0
      - libunwind-x86_64.so.8
      - libsocketcan.so.2

platforms:
  arm64:
  amd64:

apps:
  gse-app-cli:
    command: cubesupportcli
    environment:
      # Use the .NET 10 variable name
      DOTNET_ICU_VERSION_OVERRIDE: "74"
      LD_LIBRARY_PATH: $SNAP/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:$SNAP/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:$LD_LIBRARY_PATH
    plugs:
      - home
      - serial-port
      - can-bus
      - network-control

parts:
  libs:
    plugin: nil
    stage-packages:
      - libicu74
      - libunwind8
      - libssl3t64
      - liblttng-ust1t64
      - libbrotli1
      - libsocketcan2
    build-attributes:
      - enable-patchelf
    override-build: |
      craftctl default
      LIB_DIR="usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR"
      cd "$CRAFT_PART_INSTALL/$LIB_DIR"
      ln -sf liblttng-ust.so.1 liblttng-ust.so.0
      ln -sf libsocketcan.so.2 libsocketcan.so

  gse-app-cli:
    after: [libs]
    plugin: dotnet
    source: .
    dotnet-project: cubesupportcli/cubesupportcli.csproj
    dotnet-self-contained: true
    dotnet-properties:
      UseAppHost: "true"
      InvariantGlobalization: "false"
      TargetFramework: "net10.0"
      DebugType: "none"
      DebugSymbols: "false"
      CopyOutputSymbolsToPublishDirectory: "false"
    dotnet-version: "10.0"
    permissions:
      - path: cubesupportcli
        mode: "755"
    build-attributes:
      - enable-patchelf

Note that snapd’s confinement won’t override classic DAC permissions, meaning that it cannot grant regular user direct access to a certain resource if such resource is also inaccessible by the user without confinement.

There are at least two ways that may solve this issue:

  • Rewrite the HOME environment variable’s value to SUDO_HOME which is the sudo invoker’s home directory in the snap runtime.
  • Require the user to configure appropriate udev rules to allow CAN bus access by the user, then the application will no longer need to be run as root. Note that there’s currently no mechanism in snapd that facilitate this configuration so it needs to be manually done by the user.

Hi!

  1. Are you sure that rewriting $HOME to be $SUDO_USER will work? I am not sure the confined snap’s app armor permissions are updated to allow read/write to $SUDO_USER.
  2. Can you give me a hint what the udev proposal would look like ? Since CAN (or network interfaces) do not have device nodes, I cannot see how this will work.

Right now I think the most viable way is to run a service which listens on a socket for instructions to setup the CAN interface.

service: gse-app-cli --can-server (runs as root, listens on unix socket for performing netlink setup).

normal user gse-app-cli …. (CAN configure goes as request to snap service.

Your analysis is correct, snap apps running as root has no access to $SUDO_USER’s home directory via the home interface. Therefore this method is a dead end.

I have no idea as I’m not familiar with CAN. Gemini gives the following suggestion, though:

# Grant the 'canaccess' group control over all SocketCAN interfaces
SUBSYSTEM=="net", KERNEL=="can*", GROUP="canaccess", MODE="0660