Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

interfaces: mount host system fonts in desktop interface #3889

Merged
merged 5 commits into from
Sep 19, 2017

Conversation

jhenstridge
Copy link
Contributor

On classic systems, bind mount the host system fonts and fontconfig cache into the sandbox for snaps using the desktop interface. This is based on discussions on the forum here:

https://forum.snapcraft.io/t/desktop-allow-access-to-host-system-fonts/1796

The changes in this PR depend on certain directories existing in the core snap to be used as mount points. At present this is only available in the edge channel, which can be installed with:

snap refresh --channel=edge core

Here is a basic manual test plan:

  1. Install fontconfig_0.1_amd64.snap using vanilla snapd. This snap contains the fontconfig command line utilities.

  2. Run fontconfig.fc-list and note that no fonts are visible.

  3. Switch to snapd from this PR.

  4. Reconnect the desktop interface to make sure we've got the new rules:

     snap disconnect fontconfig:desktop
     snap connect fontconfig:desktop
    
  5. Run fontconfig.fc-list and note that it lists all the host system fonts.

  6. Run snap run --shell fontconfig.fc-list to open a shell within the sandbox to verify that fonts are visible under /usr/share/fonts.

  7. Run

     rm -rf ~/snap/fontconfig/current/.cache/fontconfig
     fontconfig.fc-cache -s -v
    

    This checks the validity of the caches for all system font dirs, and updates any invalid ones. You should see many "skipping, existing cache is valid" messages for the directories containing fonts.

  8. Check that no new caches were written to the ~/snap/fontconfig/current/.cache/fontconfig directory.

for _, entry := range entries {
c.Check(expectedMountPoints, testutil.Contains, entry.Dir)
c.Check(entry.Name, Equals, "/var/lib/snapd/hostfs"+entry.Dir)
c.Check(entry.Options, DeepEquals, []string{"bind", "ro"})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will probably need corresponding snap-confine apparmor profile updates. While I recall adding one for /usr/share/fonts well mid last year I'd like to check if all of those are present.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't notice any problems in my manual testing. I wonder if snap-update-ns got in first so I never hit that code path in snap-confine?

@codecov-io
Copy link

codecov-io commented Sep 8, 2017

Codecov Report

Merging #3889 into master will increase coverage by 0.04%.
The diff coverage is 87.8%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #3889      +/-   ##
==========================================
+ Coverage    75.9%   75.94%   +0.04%     
==========================================
  Files         416      416              
  Lines       35988    36016      +28     
==========================================
+ Hits        27315    27351      +36     
+ Misses       6753     6750       -3     
+ Partials     1920     1915       -5
Impacted Files Coverage Δ
dirs/dirs.go 98% <100%> (+0.08%) ⬆️
interfaces/builtin/desktop.go 86.84% <86.48%> (-13.16%) ⬇️
image/helpers.go 55.45% <0%> (-5.57%) ⬇️
logger/logger.go 92.45% <0%> (-2.9%) ⬇️
overlord/assertstate/assertstate.go 76.66% <0%> (-1.97%) ⬇️
overlord/devicestate/devicemgr.go 74.14% <0%> (-1.71%) ⬇️
cmd/snap/cmd_known.go 70% <0%> (-1.7%) ⬇️
corecfg/corecfg.go 50% <0%> (-1.62%) ⬇️
overlord/ifacestate/helpers.go 62.33% <0%> (-0.67%) ⬇️
interfaces/builtin/network.go 100% <0%> (ø) ⬆️
... and 16 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 7a4e206...1d76910. Read the comment docs.

Copy link
Contributor

@chipaca chipaca left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, but I think it's missing a part of the puzzle: a snap can have a base that ships fonts, and this doesn't seem to consider that. But I guess that can come later.

var desktopFontconfigDirs = []string{
"/usr/share/fonts",
"/usr/local/share/fonts",
"/var/cache/fontconfig",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these need to be set in dirs, so they inherit the global root dir.

Also, how cross-distro are these locations?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, what happens if a different base snap doesn't have the mount point location available? Does it fail gracefully, or does it break the mounting sequence? @zyga?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll have a look at defining these through the dirs package.

The locations are fairly consistent. /usr/share/fonts is fontconfig's default font location. The /usr/local/share/fonts directory isn't part of upstream fontconfig, but is commonly configured by major distros:

https://sources.debian.net/src/fontconfig/2.12.3-0.2/debian/rules/#L20
https://src.fedoraproject.org/rpms/fontconfig/blob/master/f/fontconfig.spec#_69
https://build.opensuse.org/package/view_file/openSUSE:Factory/fontconfig/fontconfig.spec?expand=1
https://git.archlinux.org/svntogit/packages.git/tree/trunk/PKGBUILD?h=packages/fontconfig#n44

The fontconfig cache directory follows autoconf's $localstatedir directory, which is generally always configured as /var on Linux.

As for base snaps, I can see two solutions: (1) require base snaps to create these mount points in the same way they'd create /etc, /home, etc, or (2) rely on the upcoming layouts feature to ensure the mount points are available.

Copy link
Contributor

@niemeyer niemeyer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks reasonable, in theory. Some questions above next to John's comments.

Copy link

@jdstrand jdstrand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conversion from simple style interface to complex style interface looks fine (though, please add the comment I requested).

Please make the small testsuite change I requested to make it consistent with other (modern) interface test code.

As for cross-distro, I can't answer but I can say that the method @jhenstridge came up with seems sound to me. He has a list of directories to check for existence on the host and only adds the mountpoint if they exist. I can say that looking at /etc/apparmor.d/abstractions/fonts they are consistent (the things missing here that are in the abstraction I wouldn't want to add without seeing them used in the wild, with the possible exception of /var/cache/fonts). If we are missing any for cross-distro, they can easily be added to the list in future PRs.

As for the mountpoints not being in the core snap, snap-confine has a concept for handling quirks, but I think code needs to be added to support these. I'll let @zyga comment.

Since @niemeyer is blocking on the mountpoint existence in core snap question, there is no reason for me to also block. Since my requested changes are small, marking as approved (though, again, please make the requested changes).

}

func (iface *desktopInterface) AutoConnect(*interfaces.Plug, *interfaces.Slot) bool {
return true

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment here like we do in other interfaces:

// allow what declarations allowed

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder when we can get rid of AutoConnect altogether...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment added.


// On classic systems, a number of font related directories
// are bind mounted from the host system if they exist.
release.OnClassic = true

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typically we'd do this in the testsuite instead:

// on a classic system with desktop slot coming from the core snap,
// a number of font related directories are bind mounted from the
// host system if they exist.
restore = release.MockOnClassic(true)
defer restore()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm already using MockOnClassic earlier to save the state. I'll switch this case to use MockOnClassic too, just in case someone splits the test later and leaks state.

Copy link
Contributor Author

@jhenstridge jhenstridge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a good answer to the question of base snaps wanting to ship fonts. I can imagine that being useful on Ubuntu Core systems (which the desktop interface doesn't currently work on), but why would you want to have different fonts than the host on classic?

And unless the author of the base snap ensures they cover all the world's languages, it is going to lock out some users. In contrast, we can take it as a given that the host system contains fonts capable of displaying the user's native language.

var desktopFontconfigDirs = []string{
"/usr/share/fonts",
"/usr/local/share/fonts",
"/var/cache/fontconfig",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll have a look at defining these through the dirs package.

The locations are fairly consistent. /usr/share/fonts is fontconfig's default font location. The /usr/local/share/fonts directory isn't part of upstream fontconfig, but is commonly configured by major distros:

https://sources.debian.net/src/fontconfig/2.12.3-0.2/debian/rules/#L20
https://src.fedoraproject.org/rpms/fontconfig/blob/master/f/fontconfig.spec#_69
https://build.opensuse.org/package/view_file/openSUSE:Factory/fontconfig/fontconfig.spec?expand=1
https://git.archlinux.org/svntogit/packages.git/tree/trunk/PKGBUILD?h=packages/fontconfig#n44

The fontconfig cache directory follows autoconf's $localstatedir directory, which is generally always configured as /var on Linux.

As for base snaps, I can see two solutions: (1) require base snaps to create these mount points in the same way they'd create /etc, /home, etc, or (2) rely on the upcoming layouts feature to ensure the mount points are available.

}

func (iface *desktopInterface) AutoConnect(*interfaces.Plug, *interfaces.Slot) bool {
return true
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment added.


// On classic systems, a number of font related directories
// are bind mounted from the host system if they exist.
release.OnClassic = true
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm already using MockOnClassic earlier to save the state. I'll switch this case to use MockOnClassic too, just in case someone splits the test later and leaks state.

@mvo5 mvo5 merged commit e6f3dfe into canonical:master Sep 19, 2017
@develar
Copy link

develar commented Feb 10, 2018

If anyone else will came here with question "why no fonts in my snapped app" — make sure that you did snap refresh --channel=edge core. I am not Linux user, and I have Xubuntu 16 and Ubuntu 16 installed as VM (Parallels). Despite that fact that I update my systems regularly, core snap was not updated and as result, no fonts in my app.

I am electron-builder developer. Now, if I use freshly installed Ubuntu 16, no fonts in the Electron apps — apparmor denied access to fonts.

Could you please confirm that this feature is available for Ubuntu 16 or when it will be released?

Feb 10 10:04:13 ubuntu kernel: [  293.074680] audit: type=1400 audit(1518253453.250:39): apparmor="DENIED" operation="file_mmap" profile="snap.sep.sep" name="/home/develar/.config/dconf/user" pid=3799 comm="sep" requested_mask="m" denied_mask="m" fsuid=1000 ouid=1000
Feb 10 10:04:13 ubuntu kernel: [  293.435566] audit: type=1400 audit(1518253453.611:40): apparmor="DENIED" operation="file_mmap" profile="snap.sep.sep" name="/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-R.ttf" pid=3799 comm="sep" requested_mask="m" denied_mask="m" fsuid=1000 ouid=0
Feb 10 10:04:13 ubuntu kernel: [  293.450608] audit: type=1400 audit(1518253453.626:41): apparmor="DENIED" operation="file_mmap" profile="snap.sep.sep" name="/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf" pid=3799 comm="sep" requested_mask="m" denied_mask="m" fsuid=1000 ouid=0
Feb 10 10:04:13 ubuntu dbus[690]: [system] Activating via systemd: service name='org.bluez' unit='dbus-org.bluez.service'
Feb 10 10:04:14 ubuntu kernel: [  294.567727] audit: type=1400 audit(1518253454.743:42): apparmor="DENIED" operation="file_mmap" profile="snap.sep.sep" name="/usr/share/fonts/truetype/liberation/LiberationSerif-Bold.ttf" pid=3938 comm="sep" requested_mask="m" denied_mask="m" fsuid=1000 ouid=0
Feb 10 10:04:14 ubuntu kernel: [  294.567857] audit: type=1400 audit(1518253454.743:43): apparmor="DENIED" operation="file_mmap" profile="snap.sep.sep" name="/usr/share/fonts/truetype/liberation/LiberationSerif-Bold.ttf" pid=3938 comm="sep" requested_mask="m" denied_mask="m" fsuid=1000 ouid=0
Feb 10 10:04:14 ubuntu kernel: [  294.567931] audit: type=1400 audit(1518253454.743:44): apparmor="DENIED" operation="file_mmap" profile="snap.sep.sep" name="/usr/share/fonts/truetype/liberation/LiberationSerif-Bold.ttf" pid=3938 comm="sep" requested_mask="m" denied_mask="m" fsuid=1000 ouid=0
Feb 10 10:04:14 ubuntu kernel: [  294.568000] audit: type=1400 audit(1518253454.743:45): apparmor="DENIED" operation="file_mmap" profile="snap.sep.sep" name="/usr/share/fonts/truetype/liberation/LiberationSerif-Bold.ttf" pid=3938 comm="sep" requested_mask="m" denied_mask="m" fsuid=1000 ouid=0
Feb 10 10:04:14 ubuntu kernel: [  294.568063] audit: type=1400 audit(1518253454.743:46): apparmor="DENIED" operation="file_mmap" profile="snap.sep.sep" name="/usr/share/fonts/truetype/liberation/LiberationSerif-Bold.ttf" pid=3938 comm="sep" requested_mask="m" denied_mask="m" fsuid=1000 ouid=0
Feb 10 10:04:14 ubuntu kernel: [  294.568209] audit: type=1400 audit(1518253454.743:47): apparmor="DENIED" operation="file_mmap" profile="snap.sep.sep" name="/usr/share/fonts/truetype/liberation/LiberationSerif-Bold.ttf" pid=3938 comm="sep" requested_mask="m" denied_mask="m" fsuid=1000 ouid=0
Feb 10 10:04:14 ubuntu kernel: [  294.568292] audit: type=1400 audit(1518253454.743:48): apparmor="DENIED" operation="file_mmap" profile="snap.sep.sep" name="/usr/share/fonts/truetype/liberation/LiberationSerif-Bold.ttf" pid=3938 comm="sep" requested_mask="m" denied_mask="m" fsuid=1000 ouid=0
Feb 10 10:04:18 ubuntu kernel: [  298.076893] kauditd_printk_skb: 52746 callbacks suppressed
Feb 10 10:04:18 ubuntu kernel: [  298.076894] audit: type=1400 audit(1518253458.252:52795): apparmor="DENIED" operation="file_mmap" profile="snap.sep.sep" name="/usr/share/fonts/truetype/liberation/LiberationMono-Regular.ttf" pid=3938 comm="sep" requested_mask="m" denied_mask="m" fsuid=1000 ouid=0
Feb 10 10:04:18 ubuntu kernel: [  298.076970] audit: type=1400 audit(1518253458.252:52796): apparmor="DENIED" operation="file_mmap" profile="snap.sep.sep" name="/usr/share/fonts/truetype/liberation/LiberationMono-Regular.ttf" pid=3938 comm="sep" requested_mask="m" denied_mask="m" fsuid=1000 ouid=0
Feb 10 10:04:18 ubuntu kernel: [  298.077046] audit: type=1400 audit(1518253458.252:52797): apparmor="DENIED" operation="file_mmap" profile="snap.sep.sep" name="/usr/share/fonts/truetype/liberation/LiberationMono-Regular.ttf" pid=3938 comm="sep" requested_mask="m" denied_mask="m" fsuid=1000 ouid=0
Feb 10 10:04:18 ubuntu kernel: [  298.077137] audit: type=1400 audit(1518253458.252:52798): apparmor="DENIED" operation="file_mmap" profile="snap.sep.sep" name="/usr/share/fonts/truetype/liberation/LiberationMono-Regular.ttf" pid=3938 comm="sep" requested_mask="m" denied_mask="m" fsuid=1000 ouid=0

And after system restart, fonts stopped worked again...

@develar
Copy link

develar commented Feb 10, 2018

I have added test snap to https://bugs.launchpad.net/snappy/+bug/1621568/comments/6

@jdstrand
Copy link

@develar - this is probably https://forum.snapcraft.io/t/fonts-fail-to-load-when-desktop-plug-added/3414/2 (which has information on what to do to fix your snap).

This is an electron bug that should be fixed in newer releases. See electron/electron#11628

@develar
Copy link

develar commented Feb 12, 2018

@jdstrand Thanks for answer. Not clear for me how linked issue is related to fonts issue. Anyway — I will fix electron-builder to do execstack --clear-execstack and I will test how does it fix fonts issue.

@jdstrand
Copy link

jdstrand commented Feb 12, 2018

@develar - this denial:

Feb 10 10:04:18 ubuntu kernel: [  298.077137] audit: type=1400 audit(1518253458.252:52798): apparmor="DENIED" operation="file_mmap" profile="snap.sep.sep" name="/usr/share/fonts/truetype/liberation/LiberationMono-Regular.ttf" pid=3938 comm="sep" requested_mask="m" denied_mask="m" fsuid=1000 ouid=0

is basically the same as the denials listed in the forum topic I mentioned. Specifically, you have an mmap denial on a font. This coupled with the fact that electron builds sometimes end up with executable stacks lead me to my response.

Good luck!

@develar
Copy link

develar commented Feb 20, 2018

@jdstrand Thanks, indeed, it is was a cause. electron-builder uses custom code in go to clear exec stack now (so, it works also on macOS).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants