Lol - an open source Snap server implementation

lol

About

lol is an experimental open-source implementation of the Snap server which is in beta at the moment. The Discord, Telegram, Kdenlive, Firefox and Chromium snaps have been uploaded to the demo server. I’ll be adding many more soon and you can post your snap suggestions at https://repo.lolsnap.org/lol-snap/lol-server/-/issues.

Reasons why I worked on it

Some people in the Ubuntu community were complaining about the Snap server being proprietary (although the reasons for it being proprietary have been communicated by Canonical and they seem to be valid as well), so I thought of spinning up a small implementation so that people would stop complaining about it and still enjoy snaps. It’s also quite similar to reprepro and isn’t supposed to be a complete replacement for the server, rather a tiny implementation which anyone could set up if they do not wish to go with Canonical’s brand store or Snap server for any strong reasons (such as a company having restrictions on putting their confidential data on another server). In fact, lol would also improve the adoption of Snaps outside Ubuntu and I believe Ubuntu Unity was the first amongst the Ubuntu flavours and remixes to use the Firefox snap. I’ll improve it further based on the Snapcraft team’s feedback.

Discord server

I’ll primarily be discussing the development of lol on my Discord server at: https://discord.gg/Dwj9xeZhtP

Any feedback would be appreciated.

The lol Telegram snap and the deb package are open at the same time in this screenshot:

6 Likes

Just for a feedback I once feel that we need a Snap/Snap Store alternative called Pans/Pans Store, you know, with the name spelled backward. :wink:

1 Like

Hi,

I work on the snapd team here and I’m curious if you have put any thought into how to handle strictly confined snaps distributed by “Lol” that use interfaces? As it is currently written your script lol just downloads the snap and installs the snap with --dangerous without connecting any interfaces, this is problematic for a couple of reasons:

  • Users will be left with snaps that don’t have their interface connections performed as needed for normal operations, such as for Discord access to audio output and camera will not be setup initially leading users to have to manually connect these interfaces
  • Users will not have any way to know what interfaces are reasonable to connect for snaps installed this way, since there is no validation of what interfaces should be allowed to be uploaded/used for snaps. For instance powerful and dangerous interfaces like kernel-module-control, etc. can be added to snaps that are hosted via this server implementation without any restriction and thus users are more exposed to malicious snaps
  • Users will be left on their own to figure out what dependencies need to be installed, for example the telegram-desktop snap needs to have the gtk-common-themes snap installed and those connections performed and likewise firefox needs gnome-3-38-2004 and a connection to that snap as well that is not performed by this client implementation.

Some other issues with this implementation I found:

  • Users will not get any refreshes automatically for snaps installed this way, since this store implementation is effectively entirely limited to just downloading snap binaries and calling snap install --dangerous on them, snapd does not talk to this “lol-server” at all. This means that users are left to refresh snaps themselves manually and exposed to security vulnerabilities.
  • You have chosen to include the snapd snap and base snaps like core18, core20, etc. in the default list of snaps hosted by this, which is problematic because it means that if someone uses your script to install say snapd via lol install snapd, now they will not get any updates to that snap from Canonical, and are now entirely dependent on this server to get updates to snapd, and because of above, they will not even get automatic security updates from this server, they will manually need to do refreshes and again are dependent on whoever is hosting this to refresh their repos with the new versions of snaps provided by the snapstore.

In any case, I think it’s great that you want to expand usage of snaps across the community, but I think it’s important that we don’t create a bad experience for these users who choose to use your server/client implementation here instead of sticking with the supported default versions of snaps provided by the snap store and thus vetted and updated automatically when there are security vulnerabilities.

I also don’t want this forum post to devolve into disagreement about the virtue of having an alternative snap store, so I am merely focusing here on issues that a potential user using this may run into and if you have put any thought into how to address these issues because I think that these issues should be addressed before being widely used.

Thanks,

Ian

12 Likes

Sure, thank you for your feedback. Since these 4 points are interrelated, I’ve grouped them together. lol doesn’t automatically install the core snaps if they are already installed from the Snap Store. I’m implementing the interfaces bit right away. Could you let me know how to get the list of interfaces and dependencies for a snap which need to be connected/installed automatically? It would be great if the Snapcraft team could help out in developing lol (and a Snap Store plugin for it).

I was already working on auto-refreshes in lol. I’m hoping to roll this out by the end of this week.

the information for some of this (used extensions and library snaps) is probably available via a store api query:

but info like the auto-connections of required interfaces are only handled internally through snap declarations that are put in place manually after a forum discussion … i don’t think that info is easily retrievable via the API …

2 Likes

Well this is a bit of a tricky question, since the information you refer to about what interfaces will be automatically connected is a function of quite a few things:

  • the snap-declaration assertion for the snap
  • the base-declaration that snapd has inside it
  • some system specific parameters like whether the device is classic or whether the device has slots available to auto-connect to, etc.
  • some interfaces are declared by snaps to be used, but are not automatically connected

So you can’t just parse the list of interfaces from the snap itself to figure out what permissions to grant it, this is also a security issue because now snaps uploaded to this repo/alternative store can simply declare interfaces without having been vetted. Ideally your store implementation would either use the main snapstore’s “snap assertion service” or it would have it’s own root of trust for an alternative snap assertion service.

That being said, to properly implement this, you would need to get at least the snap-declaration for a snap from somewhere and combine it on top of the base-declaration from snapd (there’s a debug command for getting the base-declaration - you should use this, since the base-declaration inside snapd often changes) and then the resulting assertion will tell you what can be auto-connected and what can be installed. The assertion part of snapd is incredibly complicated though so this will take a bit of work and (@ogra will prove me wrong on this I’m sure) probably impossible to write in a shell script. At a minimum you will need to parse YAML with nested maps and handle the fact that settings in assertions can have different types, sometimes the value for a key is a concrete type like a string (which is a regular expression), sometimes the value is a list (which means that the values in the list is an alternation, i.e. a logical OR of what the values are), or sometimes the value is a map (which means that the values in the list are required or a logical AND of the nested settings). If you’re able to do all that, then you get to the other critical part of assertions which is the fact that we don’t just use the snap-declaration blindly, we only use the snap-declaration for a given .snap blob when that .snap blob has an associated and correct snap-revision assertion (and from the snap-revision assertion we get the trust for the snap-declaration), which is another assertion signed by Canonical’s root of trust to say that the snap blob has not been tampered with at all. You could either again use Canonical’s snap assertion service to get this assertion or you would need to implement your own mechanism for folks to create their own assertion attesting to the binary bits that make up a .snap blob. If you go the latter route, you will need to rebuild snapd to add this alternate root of trust because currently snapd only ever trusts one root of trust, Canonical’s keys.

I guess one easy thing you could do is to save the .assert file alongside the .snap file on your server and have the lol client download both of them and then do snap ack foo.assert && snap install foo.snap (note the absence of the --dangerous flag), and you just use the .assert files from the Canonical snapstore. That will take advantage of all of the assertion formatting that snapd already does and thus do all of the interface logic that snapd does, but allow people to get the snap blobs initially from a source other than the store. This has perhaps the unintended side effect that now that the snap will be installed as if it was installed directly from the store that now those snaps will be updated when they are updated in the store, even if the user has not run lol refresh foo or the repo the user installed the snap from has not pushed an update there.

As to what dependent snaps should be installed, this is much easier to find out, you can inspect the snap.yaml and look for 2 things

  1. the base of the snap, if no base is set the implicit base is core. So install the base snap if it is not already installed
  2. any content interfaces which have default-provider set on them. This setting simply says to snapd “if this snap is not already installed, install it too as part of this change”.

Unfortunately I myself can’t commit any development time to this, there is a lot of things we are working on and committing time to reviewing code in depth or writing new code for this project is not high on the priority list. I’m happy to provide input in an abstract sense as to whether the implementation matches what we do with the normal snap store but that’s about the extent of what I can do to help unfortunately though you are in the right place for this sort of project as numerous other people on the forum have talked about developing just such a piece of software.

7 Likes

Was trying to figure out how to get the epoch for a snap when implementing the auto-update bit (to be able to check which version is newer). Also, am not sure what the number after the snap name when using snap download is used for.

Use snap revision for that. Check what revision your currently installed snap has, compare if the “to-be-installed” revision is higher (parse the assert file).

You can get the current revision by querying this snapd endpoint /v2/assertions/snap-declaration?snap-id=<your_snap_id>

edit: use this python example to talk to the snapd unix socket https://stackoverflow.com/a/59594889, or try curl

2 Likes

To add some background, I have done an implementation of an “overlay” snap store, that I currently can’t open up, employer policy for now.

I believe, patching snapd to allow Your custom signing authority is a good way to achieve a custom snap store, however that becomes unrealistic when You have to ask users to install Your custom build of snapd on their computers. However in our case, we build the whole OS (yocto), so patching snapd is not an issue. So I believe the most important change needs to happen in snapd to have a realistic third-party store.

Here is how we implemented it

  1. patched snapd to support custom signing authority
  2. patched snapd to only refresh snaps if the authority is canonical
  3. Wrote a django based service, that allows developers to register and upload their snaps
  4. On upload, the server unpacks the snap, extracts the snapcraft yaml and parses it and does necessary verification.
  5. Then snap sign command is used to create an assertion
  6. Finally the snap and assertion are uploaded to S3

For the install/update we have a custom cli tool that allows to install/remove snaps from our store and transparently from Canonical store as well. The current policy is to first check if a snap exists on our store and install it, if not, we call the snapd sock to install it from Canonical store.

We support both channels (but not tracks) and delta updates (using desync).

edit: Automatic updates are a TODO but we know what we have to do to achieve that. We will be using Autobahn/WAMP for push notifications to the device, however that’s currently not an immediate requirement.

2 Likes

Just to add my 2 cents of THANK YOU! Glad you are doing this work. It’s very plausible that other people with good reason want to move snaps around differently to Canonical, so it’s a fine thing that your work will enable that.

7 Likes

Thank you very much, @sabdfl :smiley:

1 Like

Hi Mark, as stated above, for a third-party snap store to have all the security features that snapd offers, snapd needs to be patched to allow custom signing authority.

Would the snapd team take patches to allow that ? If so what mechanism would that be, can the signing authority be picked from environment variable (there are security implications). Maybe snapd could have some sort of “developer mode”, which, if enabled, allows setting custom signing authority.

Here is how we patched snapd Custom assertion possible? to add our custom signing authority.

I took a look at the assert file for the hello snap and the number in the downloaded snap’s file name (hello_38.snap) looks to be the same as the snap revision in the assert file.

I got basic auto-refreshing working and was wondering if it’s possible to set up a cron job or systemd timer in a classic snap (since lol itself is packaged as a classic snap).

EDIT: Figured it out: Add support for service timers

I’ve released the second beta of lol, which includes basic support for auto-refreshing snaps. You can update an existing installation by running: lol install lol && lol refresh lol

Here’s the GitLab repo (which includes information on how to install lol): https://repo.lolsnap.org/lol-snap/lol

For support, I do have a Discord server at https://discord.gg/Dwj9xeZhtP with a channel for lol.

https://snapcraft.io/docs/snapcraft-yaml-reference

might be handy (so you don’t need to search the forum for the preceding discussion of each and every option :slight_smile: )

2 Likes

Yeah, looks to be really useful (found a related Discourse thread yesterday). Thank you for all your help, @ogra :slight_smile:

awesome job :smiley:

i hope this job makes snap adoption by other distros more welcomed :smiley:

1 Like

Is the intent to just “host” snaps that are already signed by Canonical somewhere else? I.e. a mirror? Otherwise this is a significant undertaking and requires a patched snapd as @om26er mentioned.

When developing the auto-update bit, I was trying to find out how to acquire the epoch for a snap (to be able to check which version is newer). Also, I’m not sure what the number following the snap name is for when utilizing snap download.