On writing my first snaps, and keeping my passwords secure everywhere
I’ve been writing snaps for a year, more or less. But all the snaps I had written were a one-time thing to reproduce a bug, automate a scenario, or explore a specific code path. A few months ago I decided to write one snap that I would use day-to-day. The application I use the most by far is keepass, so that seemed like a good place to start.
kpcli
With kpcli I access my password database from the command line. It’s just one big perl script, but not as ugly as it sounds :) That was a great first experiment because I haven’t tried perl in a snap before, and it would let me access the passwords from my beagle bone black in a secure and confined way.
Because it is a script, I just need to copy it into the snap. Of course, there’s a plugin for that! snapcraft is a meta-packaging tool to generate snaps. It comes with plugins for many packaging tools, copy being the most simple of them. Run:
$ sudo apt install snapcraft
$ snapcraft help copy
The next step is to look at the dependencies. Luckily, they are right there in the README file, in the form of Ubuntu packages. snapcraft calls them stage-packages, and they will be installed into the snap during the build.
Now I have almost all the difficult details to make the snap, and it was surprisingly easy. With this information, I wrote a part (read more about parts):
kpcli:
source: https://github.com/alecsammon/kpcli.git
plugin: copy
files:
kpcli.pl: scripts/kpcli.pl
stage-packages:
- perl-base
- libcrypt-rijndael-perl
- libterm-readkey-perl
- libsort-naturally-perl
- libfile-keepass-perl
- libterm-shellui-perl
- libterm-readline-gnu-perl
- libclone-perl
I am just missing a way to tell the command where to find the perl libraries that I am bundling inside the snap. There is work in progress to make it easier to set up environment variables before launching the command; but for my first simple snap, quick and easy will do. I wrote a wrapper script:
#!/bin/sh
$SNAP/usr/bin/perl -I $SNAP/usr/share/perl/5.22 -I $SNAP/usr/lib/x86_64-linux-gnu/perl/5.22 -I $SNAP/usr/lib/x86_64-linux-gnu/perl5/5.22 -I $SNAP/usr/share/perl5 $SNAP/scripts/kpcli.pl
and the corresponding part:
launcher:
plugin: copy
files:
run.sh: scripts/run.sh
Finally, I added details about the name, version, summary and description of the snap, and defined the kpcli app. You can download the source of this snapcraft.yaml, put it in a directory and run snapcraft in there to generate it yourself. Or try mine installing it from the store:
$ sudo snap install kpcli-elopio
$ kpcli-elopio.kpcli
KeepassX
Ok, that solves command line access, but I also want some eye-candy, sometimes. KeepassX gives me access to the same passwords database through a GUI. By the time I finished my CLI snap I saw work from kyrofa, sergiusens and dpm to make it easy to snap desktop apps and use them in Ubuntu classic. I also was at a sprint sitting next to attente, who knows everything I didn’t know about the desktop, so it was a great time to enter the world of collaborative snapping.
Let me digress here for a moment to talk about collaboration. snapcraft and snapd are free software, and a lot of their design is based on the idea that the system can be extended and improved through the shared work of many and diverse developers. snapcraft aims to be the universal packaging tool, and plugins are the way we integrate the wild variety of build systems into the lifecycle to generate a snap. KeepassX is more complex than the CLI client, and it uses cmake to generate the executable. Well, guess what? There’s a plugin for that! :) (see the source of the cmake plugin). Also, this application uses the QT5 UI libraries. Just like with the kpcli snap, that means I will require to set up some environment variables and a launcher; but somebody else already did that and we have a way to share and reuse parts. Run:
$ snapcraft update
$ snapcraft search qt5
$ snapcraft define qt5conf
There’s a part for that!! And when this application is running, I would like it to be integrated with unity7, if available. That means I would need a few extra permissions for my isolated app to access the unity7 services and libraries installed in a classic Ubuntu system. I’m a lucky guy because there’s an interface for that!!! (see the source of the unity7 interface) By default, snaps will be fully confined and won’t be able to access anything they don’t provide by themselves. Through interfaces, we allow snaps to get extra permissions to consume services, access devices and directories, and do anything that you could do in a classic unconfined Linux system; but now in a controlled manner with the explicit acknowledgement of the user for sensitive stuff. Run:
$ snap interfaces
We have plugins, parts and interfaces for the most common cases of the most common apps. But the fun is just starting, and there are lots and lots of apps out there that would benefit from the controlled dependencies, usable security and transactional updates of snaps. We celebrate when we get new people into the team, so if you want to participate and influence the future of this shiny project, we can help you getting started in the playpen.
One last detail about collaboration is the relation with upstreams and distribution maintainers. Snapping free software projects is a pleasure because the source is available for us to inspect and decide on the best strategy to build the snap. Then we spread the apps to more people, we get more feedback and in many cases we end up sending patches to improve the upstream project. And I already mentioned the use of Ubuntu packages during the build of the snaps. With this we bootstrap from the awesome work that the Debian and Ubuntu communities are doing, and we’ll keep benefitting from both sides as we need debs to build some snaps, and the two play nicely together.
Aaaand back on track, because with all the work shared by the community this KeepassX snap is almost done by itself, but not quite.
I start by creating a new snapcraft.yaml file an defining the snap details:
name: keepassx-elopio
version: "2.0.2"
summary: KeePassX is a cross platform password safe
description: |
KeePassX is an application for people with extremly high demands on secure
personal data management. It has a light interface, is cross platform and
published under the terms of the GNU General Public License.
Next, the app:
apps:
keepassx:
command: qt5-launch keepassx
plugs: [unity7, opengl]
Take a look at that qt5-launch, which is provided from the shared qt part that I will reference in a few lines. keepassx is a binary that lives inside the snap, and that we will compile using a snapcraft plugin. Also look at those plugs. That’s how we tell the system that a snap wants to consume an interface slot of the same name.
Followed by the parts:
parts:
keepassx:
source: https://github.com/keepassx/keepassx.git
source-tag: 2.0.2
plugin: cmake
build-packages:
- g++
- qtbase5-dev
- libqt4-dev
- libqt5x11extras5-dev
- qttools5-dev
- qttools5-dev-tools
- libgcrypt20-dev
- zlib1g-dev
stage-packages:
- libgtk2.0-0
- libqt5gui5
- unity-gtk2-module
- appmenu-qt5
- dconf-gsettings-backend
after: [qt5conf]
Note the new build-packages section. Those packages will not end up in the snap, they are only used during the compilation so they will be installed in the host where you are running snapcraft. I already discussed about the stage-packages, nothing new to see there. Now the after keyword is where a lot of the collaboration magic happens. It means that my keepassx part depends on the qt5conf part. But I don’t define the qt5conf part in my yaml file, so snapcraft will bring it from the pool of shared parts (see the source of the qt5conf part). I love this because I didn’t have to write any of the qt complications, and when a qt expert makes a change to improve it, I just need to run snapcraft again and my snap will be better with no effort. This is where the qt5-launch I used in the apps section comes from.
I finish my yaml adding a last part:
...
parts:
keepassx:
...
glibcompiledassets:
plugin: copy
files:
gschemas.compiled: usr/share/glib-2.0/schemas/gschemas.compiled
giomodule.cache: usr/lib/x86_64-linux-gnu/gio/modules/giomodule.cache
This is required to save the KeepassX settings. It’s currently not a nice way to include the compiled files in the snap, and we are discussing about how to improve it, so I won’t dig too much here. It shows how simple yet flexible snaps are: you can put anything into the snap, it doesn’t matter how you got it. But it also shows that we have things to improve, so let me insist on my invitation for you to join our project. If there’s something you hate, this is the right time to change it. If there’s something you like, there’s always room to improve it. We have cookies.
Punto final, café con tamal. Give it a try:
$ sudo snap install keepassx-elopio
$ keepassx-elopio.keepassx