How to cache snap downloads and save bandwidth

For many people, fast broadband connection and unlimited data are a reality. For others, they are not. If you have several Linux hosts in your (home) environment, and you’re using snaps, each of these systems will separately communicate with the Snap Store and periodically download necessary updates. This can be costly in terms of inbound data.

A solution to this problem is to cache snap downloads – grab the snaps once and then reuse as many times as needed. There are two principal ways to achieve this. One, you can manually download the needed snaps on a single host and then distribute them across your internal network using a custom mechanism. The downside of this approach is that you will need to maintain your own regimen of updates. Two, you can set up a snap proxy server. In this guide, we’ll show you how to accomplish this.

Prerequisites

Please note this is a somewhat lengthy and complex tutorial. We will dabble in concepts like nginx Web server, postgresql database, networking proxying, services management, certificates, and similar. Each of these requires some familiarity with the subject matter, especially if you need to troubleshoot potential errors.

Moreover, you may need to take into account network security, as running a proxy server brings its own challenges. On internal networks, this is less of an issue, but if you expose your machine to the Internet, you will need to exercise a range of healthy practices and rigorous security hardening.

Step 1: Configure postgresql database

Before you can configure the snap proxy, you need a database configuration. Install the postgresql package in your distribution. Then, to make it work with the snap proxy, the simplest script template you can use is as follows:

CREATE ROLE "snapproxy-user" LOGIN CREATEROLE PASSWORD 'snapproxy-password';
CREATE DATABASE "snapproxy-db" OWNER "snapproxy-user";
\connect "snapproxy-db"
CREATE EXTENSION "btree_gist";

Then, save the file, and run the script:

sudo -u postgres psql < proxydb.sql
CREATE ROLE
CREATE DATABASE
You are now connected to database "snapproxy-db" as user "postgres".
CREATE EXTENSION

Next, set the database connection string – you will be asked to provide the password you configured in the template script above.

sudo snap-proxy config proxy.db.connection="postgresql://snapproxy-user@localhost:5432/snapproxy-db"
Authentication error with user snapproxy-user.
Check the user name and password and that the user has the LOGIN privilege.
Please enter password for database user snapproxy-user (attempt 1 of 3):
Configured database for snaprevs role.
Configured database for snapauth role.
Configured database for snapident role.
Configured database for snapassert role.

Step 2: Install snap-proxy-server snap

The next step is to install the snap proxy, and configure it.

sudo snap install snap-store-proxy

Once the snap is in place, you will need to configure your domain. This should be a routable network address for your clients – IP address or FQDN. How you manage the namespace is entirely up to you.

sudo snap-proxy config proxy.domain="DOMAIN"

For instance:

sudo snap-proxy config proxy.domain="10.0.2.15"

Step 3: Import certificates and register your snap proxy

Once the above step is complete, you will need to make your snap proxy instance recognizable and verified by the upstream Snap Store.

sudo snap-proxy import-certificate --selfsigned
sudo snap-proxy register

The registration process is interactive. You will be asked for your Ubuntu SSO email and password, and optionally a second factor one-time key. After that, the script will ask you a number of questions on what and how you intend to use your proxy:

Please enter the Ubuntu SSO account you wish to use to register this proxy.
Email: igor@canonical
Password:
Second-factor auth: 123456
Please let us know some details of your use-case:
Primary reason for using the Proxy:
  1: Update control of snap revisions
  2: Edge proxy for constrained network access
  3: Fun!
  4: Other
>

If everything goes well, you should see a message like the one below:

Thank you, you have registered proxy ID kEpuqguXTNRB4UK9LC6Nl6Pn7ibYJtr8.
Your proxy has been automatically approved, and is ready to use.

Step 4: Add proxy configuration

Now, we need to tell the proxy to cache downloads. The configuration file will have to be stored under:

/var/snap/snap-store-proxy/current/nginx/snap-proxy.conf

In this file, add the following text – please replace the generic placeholder PROXY_HOSTNAME with your domain, e.g.: 192.168.2.122, localhost, or something else you’ve configured early on.

upstream thestore {
        server api.snapcraft.io:443;
}

server {
    listen       80  default_server;
    server_name  _;
    return       444;
}

server {
        listen 80;
        listen [::]:80;
        server_name PROXY_HOSTNAME; # hostname of your cloud proxy
        return 301 https://PROXY_HOSTNAME$request_uri;
}

server {
        server_name PROXY_HOSTNAME; # hostname of your cloud proxy
        listen 443 ssl;
        ssl_certificate /etc/ssl/certs/cert.crt;
        ssl_certificate_key /etc/ssl/private/key.key;
        location / {
                proxy_pass      https://thestore;
                # Substitute all store downloads url returned in JSON responses
                # from the store and repoint them at this cloud proxy.
      # Requires ngx_http_substitutions_filter_module that comes with nginx-extras in Debian/Ubuntu.
                subs_filter_types application/json;
                subs_filter     https://api.snapcraft.io/api/v1/snaps/download/ https://PROXY_HOSTNAME/api/v1/snaps/download/;
        }

        location /api/v1/snaps/download/ {
                proxy_pass      https://thestore;
                # Trap redirects from the download endpoint and stream the
                # response from the cdn via an internal handler @handle_cdn.
                proxy_intercept_errors on;
                error_page 302 = @handle_cdn;
        }

        location @handle_cdn {
                Internal;
                # Resolver for the store cdn hosts. Test and set appropriately.
                resolver 127.0.0.53;
                set $cdn_url $upstream_http_location;
                proxy_pass $cdn_url;
        }
}

For example, using the IP address 10.0.2.15, this line:

return 301 https://PROXY_HOSTNAME$request_uri;

Becomes:

return 301 https://10.0.2.15$request_uri;

Step 5: Verify that the proxy works correctly

To function as required, the proxy will need to be able to contact the upstream Snap Store, but also identify itself correctly, with the right assertion key that matches your registered proxy ID.

First, check that the snap proxy is running, and all its services are in the active state:

sudo snap services snap-store-proxy
Service                        Startup  Current  Notes
snap-store-proxy.memcached     enabled  active   -
snap-store-proxy.nginx         enabled  active   -
snap-store-proxy.snapassert    enabled  active   -
snap-store-proxy.snapauth      enabled  active   -
snap-store-proxy.snapdevicegw  enabled  active   -
snap-store-proxy.snapident     enabled  active   -
snap-store-proxy.snapproxy     enabled  active   -
snap-store-proxy.snaprevs      enabled  active   -

Second, run the snap-proxy status command:

snap-proxy status
Store ID: kEpcqguXTNRM4UK9LC6Nl5Mn5ibYLtr7
Status: approved
Connected Devices (updated daily): 0
Device Limit: 5
Internal Service Status:
  memcached: running
  nginx: running
  snapauth: running
  snapdevicegw: running
  snapdevicegw-local: running
  snapproxy: running
  snaprevs: running

And finally, check the connection:

snap-proxy check-connections
http: https://dashboard.snapcraft.io: OK
http: https://login.ubuntu.com: OK
http: https://api.snapcraft.io: OK
postgres: localhost: OK
All connections appear to be accessible

Step 6: Configure your clients

We now need to tell our client systems that they should use the proxy. To that end, we will need to grab the assertion key and the proxy ID on the server.

curl -sk https://DOMAIN/v2/auth/store/assertions

Replace the DOMAIN placeholder with the domain you have used to configure your proxy. The command should return some data. Something like:

curl -sk https://localhost/v2/auth/store/assertions

Or perhaps:

curl -sk https://10.0.2.15/v2/auth/store/assertions
type: account-key
authority-id: canonical
revision: 2
public-key-sha3-384:
BWDEoaqyr25nF5SNCvEv2v7QnM9QsfCc0PBMYD_i2NGSQ32EF2d4D0hqUel3m8ul
account-id: canonical
name: store
since: 2016-04-01T00:00:00.0Z
body-length: 717
sign-key-sha3-384: -CvQKAwRQ5h3Ffn10FILJoEZUXOv6km9FwA80-Rcj-f-6jadQ89VRswHNiEB9Lxk
AcbBTQRWhcGAARAA0KKYYQWuHOrsFVi4p4l7ZzSvX7kLgJFFeFgOkzdWKBTHEnsMKjl5mefFe9ji
qe8NlmJdfY7BenP7XeBtwKp700H/t9lLrZbpTNAPHXYxEWFJp5bPqIcJYBZ+29oLVLN1Tc5X482R
vCiDqL8+pPYqBrK2fNlyPlNNSum9wI70rDDL4r6FVvr+osTnGejibdV8JphWX+lrSQDnRSdM8KJi
UM43vTgLGTi9W54oRhsA2OFexRfRksTrnqGoonCjqX5wO3OFSaMDzMsO2MJ/hPfLgDqw53qjzuKL
...

Now, repeat the command and save the assertion key into a file, e.g.:

curl -sk https://192.168.1.109/v2/auth/store/assertions > proxy.assert

Similarly, grab the proxy ID:

snap-proxy config internal.store.id

Copy the relevant information (and the assertion file) to your clients. And then, on each client, run:

sudo snap ack proxy.asset
sudo snap set core proxy.store=”PROXY ID”

For example, you may have three systems in your environment, 192.168.2.100, 192.168.2.101, and 192.168.2.102. You can configure the first to be the server (use 192.168.2.100 for the snap-proxy configuration), and then your two clients will be .101 and .102 systems. These hosts need to be able to communicate with one another.

Now, you can install snaps on your clients. For each requested snap, the first download will necessarily have to take place, but then, it will be cached on the server.

Possible errors

As this is a non-trivial setup, there could be quite a few errors and snags.

Nginx not running

There could be multiple reasons why the Web server might not be running. You may have something else bound to ports 80, 443 (like a different Web server instance), or the configuration may be incorrect, for various reasons.

snap-proxy status
Store ID: kEpuqguXTNRM4UK5LC7Nl5Pn5ibZLtr2
Status: approved
Connected Devices (updated daily): 0
Device Limit: 5
Internal Service Status:
  memcached: running
  nginx: not running: (104, 'ECONNRESET')
  snapauth: running
  snapdevicegw: running
  snapdevicegw-local: running
  snapproxy: running
  snaprevs: running

Other errors you may encounter could be:

nginx: not running: [Errno 111] Connection refused
nginx: not running: hostname '10.0.2.15' doesn't match either of '10.0.2.15', '127.0.0.1', '127.0.0.1'

The first error would indicate that the necessary ports are not available (perhaps already in use). The second error would indicate a mismatch between the Web server configuration and the snap-proxy configuration.

You can check and change the nginx configuration (inside the snap) under:

/var/snap/snap-store-proxy/current/nginx/nginx.conf

Of course, you will need some level of familiarity with Web servers in general, and nginx in particular, to be able to make the relevant adjustment. Then, restart (and/or reload) the server after any change:

sudo snapctl restart snap-store-proxy.nginx

Invalid certificate

On your client, when you try to install a snap, you may get a warning that you’re using a wrong certificate:

snap install vlc
error: cannot install "vlc": Post https://10.0.2.15/v2/snaps/refresh: x509: certificate is valid for 127.0.0.1, not 10.0.2.15

This could stem from a misconfiguration in the Web server or the proxy, not unlike having a certificate for say domain.com, but not for www.domain.com. You will need to adjust the nginx configuration, and/or reconfigure (and re-register) your proxy.

To re-register the proxy, run:

sudo snap-proxy reregister

You will need to rerun the client side commands again. Similarly, you may also see:

error: cannot install "vlc": Post https://127.0.0.1/v2/snaps/refresh: x509: certificate signed by unknown authority

If this happens, it might be because you’re using custom certificates, and/or your certificates have not been added during the proxy configuration step. You can tell the proxy to use any certificate that exists under your nginx directory, e.g.:

sudo snap set system store-certs.cert1="$(cat /var/snap/snap-store-proxy/current/nginx/127.0.0.1.cert)"

Similarly, you can reset the manually added certificates:

sudo snap-proxy remove-ca-certs

For information, please take a look at the snap proxy troubleshooting tutorial.

Summary

Caching snaps can be a rather useful functionality, especially in environments with lots of clients and limited network bandwidth. At home, the snap proxy is limited to five devices, but that should still give you some leverage toward managing snap installations and updates in an efficient way. Hopefully, this tutorial clarifies some of the less obvious elements of the snap proxy setup. If you have any questions or ideas, please join our forum, and let us know.

Unsplash.

Ubuntu cloud

Ubuntu offers all the training, software infrastructure, tools, services and support you need for your public and private clouds.

Newsletter signup

Select topics you're
interested in

In submitting this form, I confirm that I have read and agree to Canonical's Privacy Notice and Privacy Policy.

Related posts

A technical introduction to the Snap Store Proxy

In the world of IoT, it is crucial to be fully in control of your devices. Over-the-air (OTA) updates are essential for a distributed set of devices. This...

Top 10 apps for a fresh Linux install in 2021

Are you struggling with too much lag? Is it time for a spring clean and a fresh Linux install? Ready to upgrade to Focal Fossa (LTS) or Hirsute Hippo?...

How’s my snap faring on different distributions?

The life of an application can roughly be divided into two: everything that happens before it goes live – building, packaging, publication – and then,...