Download snap and assert without `snap download` command

We are building a Yocto-based image, which comes with snapd preinstalled (fully confined). Now we need to include a few snaps in the rootfs. The easier way would be to just use snap download <snap_name>, however that won’t be possible because snapd is not available in our build environment. I am assuming we could get away with using curl/jq by calling a similar endpoint like https://search.apps.ubuntu.com/api/v1/package/firefox, however where do we get the .assert file ?

This may actually be part of a larger problem i.e. what to do once we have the snap downloaded, how do we install it on first boot (or during image build). Also, we are going to have our rootfs read-only, then how are subsequent updates going to work (bind-mount a rw location to /var/lib/snapd/snaps, but creating a mix of pre-seeded snaps and to-be-downloaded snaps would be a challenge)

You should be able to use the API directly, and poke http://api.snapcraft.io/docs/assertions.html to obtain the respective assertions.

Looking at snap download it gets the following:

  • snap revision from: https://api.snapcraft.io/api/v1/snaps/assertions/snap-revision/<sha-3-384 of the snap>
  • snap declaration from: https://api.snapcraft.io/api/v1/snaps/assertions/snap-declaration/<series: 16 for now>/<snap id>
  • account-key from https://api.snapcraft.io/api/v1/snaps/assertions/account-key/<sign-key-sha3-384 of the snap revision>

All of this ends up in a single file, usually <snap>_<rev>.snap

Try running SNAPD_DEBUG=1 snap download pc to see which endpoints are hit.

As for first boot, I would suggest you look into seeding, which unfortunately is completely undocumented, but the following should help you started:

  1. obtain a classic model assertion:
    snap known --remote model series=16 brand-id=generic model=generic-classic > classic.model
  2. prepare the seed:
    snap prepare-image --classic --arch=<eg. amd64> classic.model $TARGET_DIR --snap foo --snap bar

This will fetch the classic model assertion and prepare a seed suitable for use with the target system. You just need to ensure that the $TARGET_DIR/var/lib/snapd structure is present in the target image. On startup snapd should locate the seed and preload the snaps.

As for writable locations, the list of writable locations on UC20 should give you some ideas: https://github.com/snapcore/core20/blob/master/static/etc/system-image/writable-paths

Another alternative is to just build the snap binary and run it with snap download specifically, in fact we specifically support building just the snap binary on unsupported architectures i.e. macOS so that snaps and assertions can be downloaded this way (mainly to support snapcraft, but this is a valid use case as well I think).

I think someone should create a mirror, just for that i.e. publish snap binaries, which different scripts could just download and run for commands that don’t depend on the daemon.

Thanks @mborzecki for the response.

Since I don’t necessarily need the .assert file independently, because “seeding” is the only thing that I need to do, I guess I’d have to find a way to use the snap CLI as @ijohnson suggested.

As expected the extracted snap file from snapd deb for arm64 didn’t work on the Yocto image because of having a different toolchain as compared to Ubuntu. So now I need to find a way to build the CLI tool only. @ijohnson any pointers towards that would be helpful.

to build the snap binary to be usable in this different toolchain environment you can compile the snap executable statically with the following steps on i.e. Ubuntu on arm64:

  1. install Go however you choose (i.e. tarball from golang.org/dl or the snap)
  2. install pkg-config gcc libseccomp-dev
  3. setup GOPATH, i.e. GOPATH=$HOME/go; mkdir -p $GOPATH; PATH=$PATH:$GOPATH/bin
  4. get the snapd sources, mkdir -p $GOPATH/src/github.com/snapcore; cd $GOPATH/src/github.com/snapcore; git clone https://github.com/snapcore/snapd
  5. initialize vendor dependencies: cd $GOPATH/src/github.com/snapcore/snapd; ./get-deps.sh
  6. build the snap command statically: go build -o snap-exe -buildmode=default -ldflags '-linkmode external -extldflags "-static"' -tags nomanagers,nosecboot ./cmd/snap, note this will complain about some things, but for usages of snap download it doesn’t much matter

Now you have a statically linked snap-exe which can be used on any Linux pretty much for at least snap download, though it may not work for other commands. You may even be able to cross compile this way, though I didn’t test that out. That would require more options when compiling.

Hope this helps!

So I was able to seed a few snaps after using your suggestion and the snap-exe I built using @ijohnson’s instructions.

Few observations:

  • Seeding fails if clock time is incorrect, in my case it was somewhere in 1970
  • Must seed core snap, even though all my snaps use core20

What does your model assertion look like? It’s possible you are using an old style model assertion for UC16ish devices when you should be using one that is UC18ish and allows you to just use the snapd snap instead of the core snap.

I downloaded it using this command

snap known --remote model series=16 brand-id=generic model=generic-classic > classic.model
type: model
authority-id: generic
series: 16
brand-id: generic
model: generic-classic
classic: true
timestamp: 2017-07-27T00:00:00.0Z
sign-key-sha3-384: d-JcZF9nD9eBw7bwMnH61x-bklnQOhQud1Is6o_cn2wTj8EYDi9musrIT9z2MdAa

AcLBXAQAAQoABgUCWYuXiAAKCRAdLQyY+/mCiST0D/0XGQauzV2bbTEy6DkrR1jlNbI6x8vfIdS8
KvEWYvzOWNhNlVSfwNOkFjs3uMHgCO6/fCg03wGXTyV9D7ZgrMeUzWrYp6EmXk8/LQSaBnff86XO
4/vYyfyvEYavhF0kQ6QGg8Cqr0EaMyw0x9/zWEO/Ll9fH/8nv9qcQq8N4AbebNvNxtGsCmJuXpSe
2rxl3Dw8XarYBmqgcBQhXxRNpa6/AgaTNBpPOTqgNA8ZtmbZwYLuaFjpZP410aJSs+evSKepy/ce
+zTA7RB3384YQVeZDdTudX2fGtuCnBZBAJ+NYlk0t8VFXxyOhyMSXeylSpNSx4pCqmUZRyaf5SDS
g1XxJet4IP0stZH1SfPOwc9oE81/bJlKsb9QIQKQRewvtUCLfe9a6Vy/CYd2elvcWOmeANVrJK0m
nRaz6VBm09RJTuwUT6vNugXSOCeF7W3WN1RHJuex0zw+nP3eCehxFSr33YrVniaA7zGfjXvS8tKx
AINNQB4g2fpfet4na6lPPMYM41WHIHPCMTz/fJQ6dZBSEg6UUZ/GiQhGEfWPBteK7yd9pQ8qB3fj
ER4UvKnR7hcVI26e3NGNkXP5kp0SFCkV5NQs8rzXzokpB7p/V5Pnqp3Km6wu45cU6UiTZFhR2IMT
l+6AMtrS4gDGHktOhwfmOMWqmhvR/INF+TjaWbsB6g==
```

Here is what snap warnings shows, expected ?

photo_2020-11-03_03-18-19

One other question, does the “seeding” happen offline or does it require a working internet. I am thinking if I can set a somewhat “reasonable” date on our yocto image by default, will the seeding work even in the absence of internet ?

Tested, internet is not required, just need to have a “sane” time set.

So the only issue for now is to figure out a way to not require core snap for seeding.

We will try to “fix” the time on the image somehow.

ping @ijohnson, if you have some time now, can you please help with that ?

What snaps are you seeding the device with? Do you have any snaps that specify base: core or specify no base at all? If so then you need to also seed the core snap onto the device.

We are seeding crossbarfx snap, which has base: core20. Any other snaps that we’ll ship will also have base: core20. We could obviously change our snap to base: core18 if needed.

Are you including the snapd snap in the seed as well?

2 Likes

This is it. I had to ship snapd and it fixed the issue. Thanks a lot.

2 Likes