Access to system header files for a strictly confined snap

I have snapped ccls as a strict-mode snap but it seems this requires access to the various system headers under /usr/include to operate correctly. Whilst this can clearly be achieved with a classic snap, this grants far too much authority than is really required - instead I had hoped to be able to use system-files to grant read to /usr/include and /usr/local/include but this doesn’t work either - is classic the only option or can system-files be augmented to support this use-case?

diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index 2206c36..c7894ae 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -25,12 +25,22 @@ description: |
   complete. Saving files will incrementally update the index.
 
 grade: stable
-confinement: classic
+confinement: strict
 license: Apache-2.0
 
+plugs:
+  system-headers:
+    interface: system-files
+    read:
+      - /usr/include
+      - /usr/local/include
+
 apps:
   ccls:
     command: ccls
+    plugs:
+      - system-headers
+      - home
 
 parts:
   ccls:
amurray@sec-disco-amd64:~$ snap connections ccls
Interface     Plug                 Slot   Notes
home          ccls:home            :home  -
system-files  ccls:system-headers  -      -
amurray@sec-disco-amd64:~$ sudo snap connect ccls:system-headers 
amurray@sec-disco-amd64:~$ snap run --shell ccls
amurray@sec-disco-amd64:/home/amurray$ find /usr/include/ 
/usr/include/
/usr/include/sudo_plugin.h

The system-files interface as you’ve used it will only grant access to those paths as they appear in the snap’s mount namespace. So it obviously doesn’t give you what you want.

The host file system is visible within the sandbox as /var/lib/snapd/hostfs though, but access is generally forbidden by the AppArmor rules. You could punch a hole with the system-files interface though. Something like:

system-headers:
  interface: system-files
  read:
    - /var/lib/snapd/hostfs/usr/include
    - /var/lib/snapd/hostfs/usr/local/include

Assuming you can adjust the default include path for the tool (via an environment variable or default arguments, maybe?), this might suit your requirements.

The description of system-files in The system-files interface appears to indicate you can access files from the host system directly - but clearly this is not the case - so are you saying that only certain paths get added to a snap’s mount namespace (and so I am guessing /usr/include etc is not part of this - although clearly /usr/include/sudo_plugin.h is…?)

Is it possible to have the entire host file-system appear in the snap’s mount namespace at / without having to go via /var/lib/snapd/hostfs (since I don’t think it is feasible for applications to have to handle this in some transparent manner - ie. the ccls application would have to be snap-aware to translate /usr/include/foo -> /var/lib/snapd/hostfs/usr/lib/foo).

IMO it would be great if the system-files implementation be changed to ensure that anything listed via system-files gets added to the snaps mount namespace automatically as I suspect this is how most snap developers would expect it to operate. @jdstrand @pedronis can you comment?

Only certain paths from the host system are mounted over the top of the base snap. You can see the list in the code here:

I suspect the primary use cases for system-files was to access files in these locations (e.g. /etc config files, device nodes, or /proc or /sys paths).

There is no way to expose the entire host file system at / for strict confined snaps: in fact, that would likely make it impossible to provide “build once run anywhere” features of snaps, since different distros have different collections of libraries installed.

If the tool you’re packaging respects the same environment variables as GCC, you might be able to use the C_INCLUDE_PATH or CPLUS_INCLUDE_PATH variables to adjust the default include path to use the hostfs paths.

Thanks for the details @jamesh - that list seems pretty arbitrary - why expose some of the host system data (particularly /etc) and not other parts within the snap’s mount namespace? I understand different distros have different libs etc - but also different distros will have different layouts and things in /etc - so why is that more special? The idea for this use-case is to use whatever is on the host without having to know in advance. It feels like the only real solution we have for this is classic at the moment which is a shame since this is a real sledgehammer approach - it would be much nicer to be able to poke arbitrary holes into the host filesystem via system-files since I suspect this is not the first use-case like this that will come up.