Manually snap packaging Transmission
This is part of a series describing how I got Transmission compiled and running for Snappy Ubuntu Core.
Snap packaging
Having Transmission compiled binaries, we can now build our first snap package1. For that we will need the following extra files: package.yaml
(for the package metadata) and readme.md
.
The package directory/files structure will be:
meta/
package.yaml
readme.md
transmission.png
web/* # HTML/static assets for the web UI (copied from the transmission build)
x86_64/
lib/* # compiled dependencies/libraries (copied from the deps build)
transmission-daemon # binary for the daemon (copied from the transmission build)
settings.json
transmission-daemon # custom script to run the daemon in snappy
Note the above is just how I decided to organize it, but you can change/adapt as desired. The only required bits are the meta directory and the package.yaml
and readme.md
files in there.
A wrapper script to run the daemon is added to define custom environment variables23 to run Transmission daemon taking into account Snappy filesystem structure4. Also, a custom default settings file is provided that enables access to web UI from the local network.
If you take a look, this script will also handle multiple architecture binaries (that will be useful once we compile Transmission for ARM, for example).
#!/bin/bash
ARCH=$(uname -m)
PATH=${SNAP_APP_PATH}/${ARCH}/lib/:$PATH
LD_LIBRARY_PATH=${SNAP_APP_PATH}/${ARCH}/lib/:$LD_LIBRARY_PATH
export PATH LD_LIBRARY_PATH
TRANSMISSION_HOME=${SNAP_APP_DATA_PATH}
TRANSMISSION_WEB_HOME=${SNAP_APP_PATH}/web
export TRANSMISSION_HOME TRANSMISSION_WEB_HOME
DEFAULT_SETTINGS=${SNAP_APP_PATH}/settings.json
SETTINGS_FILE=$TRANSMISSION_HOME/settings.json
if test -f $SETTINGS_FILE;
then echo "Using existent settings";
else cp $DEFAULT_SETTINGS $SETTINGS_FILE; fi
# fire the binary
exec ${SNAP_APP_PATH}/${ARCH}/transmission-daemon -f --logfile "${SNAP_APP_DATA_PATH}/transmission-daemon.log"
# never reach this
exit 1
Back to snap packaging, the minimal package.yaml
5 for our Transmission package could look something like:
name: transmission
icon: meta/transmission.png
vendor: matiasb <xxxxx@xxxx.com>
version: 2.84.2
architectures: [amd64]
services:
- name: transmission-daemon
description: Transmission bittorrent client daemon.
start: ./transmission-daemon
caps:
- networking
- network-service
- network-client
ports:
external:
ui:
port: 9091/tcp
negotiable: no
peers_listening:
port: 51413/tcp
negotiable: no
As you can see we are providing our package metadata (icon, version, architectures, etc) and describing how the service is run.
Since the daemon will act as a server and a client, and require access to networking capabilities, we set the respective caps
property. We also declare the ports
we will need to be open, for the web UI (under the ui
label, convention used by webdm to link to the web app) and for the (optional) peers listening.
To build our package, in the package root directory, run:
$ snappy build
Generated 'transmission_2.84.2_amd64.snap' snap
If everything went ok, you can push it to your Snappy instance as we see in the first post:
$ snappy-remote --url=ssh://10.0.3.202 install transmission_2.84.2_amd64.snap
=======================================================
Installing transmission_2.84.2_multi.snap from local environment
Installing /tmp/transmission_2.84.2_multi.snap
2015/12/26 20:02:25.892810 verify.go:85: Signature check failed, but installing anyway as requested
Name Date Version Developer
ubuntu-core 2015-09-17 5 ubuntu
transmission 2015-12-26 IIdOOefXdbVe sideload
webdm 2015-06-14 0.9 ubuntu
generic-amd64 2015-06-05 1.1.1 ubuntu
=======================================================
Security issues
We have Transmission daemon running, but you will notice a few issues: the web UI freezes, and checking the logs 6 you will see apparmor denials when trying to access mounts and when attempting to do quotactl
syscalls.
This is because our package runs confined, and access to some resources is not allowed by default, for security reasons. You could customize apparmor and security profiles to get access to those resources7, but this will trigger a manual review when uploading the package to the store and it will likely be rejected, since you will be breaking the default confinement. So, instead, let’s try to find a work around.
Checking Transmission source code (this file: libtransmission/platform-quota.c
), you will notice that mounts
and quotactl
calls are related to the free space checks the daemon does. You can also see that there are different implementations for different platforms, guarded by compilation flags. Then, we can tweak our compilation flags to use a different implementation, particularly the one based on statvfs
, which works without any extra requirement in Snappy, and so it won’t require a custom profile and/or trigger a manual review.
After re-compiling with those changes, re-packaging and pushing to our Snappy instance, everything works as expected and there is no error in the logs!
Exposing other binaries
If you want, you could also provide extra binaries (and/or services) in your package. For example, we could also include and provide binaries for the extra Transmission tools, like transmission-remote
.
For that, we will be updating our package.yaml
and add the required extra files: the compiled binary and a wrapper script:
meta/
package.yaml
readme.md
transmission.png
web/*
x86_64/
lib/*
transmission-daemon
transmission-remote # binary for the remote tool (copied from the transmission build)
settings.json
transmission-daemon
transmission-remote # script to run remote, similar (simplified) to the one for daemon
In the package.yaml
description we add a new binaries
keyword where we list our new executable entry:
name: transmission
icon: meta/transmission.png
vendor: matiasb <xxxxx@xxxx.com>
version: 2.84.2
architectures: [amd64]
services:
- name: transmission-daemon
description: Transmission bittorrent client daemon.
start: ./transmission-daemon
caps:
- networking
- network-service
- network-client
ports:
external:
ui:
port: 9091/tcp
negotiable: no
peers_listening:
port: 51413/tcp
negotiable: no
binaries:
- name: remote
exec: transmission-remote
caps:
- networking
- network-client
Repackaging and pushing to our instance will make the transmission.remote
executable available.
Coming next
And that’s it, we have a working Transmission snap package for amd64. Next steps will be:
- Build Transmission for ARM
- Build multi-architecture snap package
- Build Transmission using snapcraft
-
https://developer.ubuntu.com/en/snappy/guides/packaging-format-apps/ ↩
-
https://trac.transmissionbt.com/wiki/EnvironmentVariables ↩
-
https://developer.ubuntu.com/en/snappy/guides/security-policy/ ↩
-
https://developer.ubuntu.com/en/snappy/guides/filesystem-layout/ ↩
-
https://developer.ubuntu.com/en/snappy/guides/package-metadata/ ↩
-
see Debugging section, https://developer.ubuntu.com/en/snappy/guides/security-policy/ ↩
-
https://developer.ubuntu.com/en/snappy/guides/security-policy/ ↩
Comments