Wednesday, April 27, 2016

Anatomy of a snappy interface

This post is the third in a series about snappy interfaces. Knowledge presented in posts one and two is assumed.

Today we will look at what makes an interface. This post might be a bit heavier on the programming side so feel free to skip over the code fragments if that is not your thing. Note that we will not build an actual interface just yet. The goal of this article is to set the stage for that to be meaningful, at least in part.

The Interface interface

From the point of view of snappy, an Interface is a bit of code with specific APIs. In go's terms it is an interface (note the lower case i). If you are familiar with other languages it is just a way to describe a class with a specific set of methods.

In go, this is spelled out as:

type Interface interface {
    ...
}


This can be read as "the go type Interface is an object with the following methods ..."

Interface name

At a very basic level each interface has a name.

type Interface interface {
    Name() string
    ...
}

That is, having an arbitrary interface you call the Name method to obtain the name of that interface. Interface name must be unique and is something that other developers will refer to so plan ahead and pick a good, descriptive name. You cannot just change it later.

Validating plugs and slots

Two of the methods in an Interface are used to verify if a plug or slot definition is correct.

type Interface interface {
    ...
    SanitizePlug(plug *Plug) error
    SanitizeSlot(slot *Slot) error
    ...
} 
 
Remember that plugs and slots can hold arbitrary attributes. A particular interface, say, one that allows access to a GPIO pin, can use an attribute to describe which particular pin is exposed. As an interface author you should check if the pin is specified correctly (e.g. that it is a number, that it has a sensible value, etc).

Both methods take an object to sanitize (a plug or a slot) and return an error if the object is incorrect. If you don't need to check anything just return nil and carry on.

Interfaces and snippets

Having a valid plug and slot, the main thing that interfaces do is to influence the various security systems. This is implemented as a set of four methods. Before I will spill the beans on the code I will explain this informally.

Small digression, when you see a security system below think of things like apparmor and seccomp. I will focus on security systems in a dedicated instalment. For now they are simply a part of the overall security story. 

Each security system is responsible for setting up security for each app of each snap. By default all apps get the same treatment, there is nothing unique about any particular app. Interfaces allow to pass extra information to a particular security system to let a particular app do more than it could otherwise.

This extra information is exchanged as snippets. Snippets are just bits of untyped data, blobs, sequences of bytes. In practice all current snippets are just pieces of text that are easy to read and understand.

Interfaces can hand out snippets for each of the four distinct cases:
  1. the mere fact of having a plug of a given interface
  2. the fact of having a particular plug connected to a particular slot
  3. the mere fact of having a slot of a given interface
  4. the fact of having a particular slot connected to a particular plug
Note that there is a pattern. Applications can get extra permission by simply having a specific plug or a specific slot. Applications can also get extra permission by making an interface connection between a plug and a slot.

Typically most permissions will be based around a plug connected to a slot. Apps bound to the plug will be allowed to talk to a specific socket, to a specific DBus object, to a specific device on the system. All such permissions will be expressed through a snippet provided by case 2 in the list above.

For applications providing services to other snaps (e.g. bluez, network-manager, pulseaudio, mir, X11) the mere fact of having a slot will grant permissions to create the service (to configure network, manage GPUs, etc). Applications like this will use the mechanism provided by case 3 in the list above.

The meaning of the snippets is almost opaque to snappy. Snappy collects them, assembles them together and hands them over to security backends to process. At the end of the day they end up as various configuration files.

So how does the method definition look like? Like this:

type Interface interface {
    ...
    PermanentPlugSnippet(plug *Plug, securitySystem SecuritySystem) ([]byte, error)
    ConnectedPlugSnippet(plug *Plug, slot *Slot, securitySystem SecuritySystem) ([]byte, error)
    PermanentSlotSnippet(slot *Slot, securitySystem SecuritySystem) ([]byte, error)
    ConnectedSlotSnippet(plug *Plug, slot *Slot, securitySystem SecuritySystem) ([]byte, error)
    ...
}

Note that each method gets the name of the security system as an argument. A single interface can influence all security systems if that is required for it to operate correctly.

Connecting interfaces automatically

The one last thing an interface can do is say it wants to automatically connect plugs to viable slots under certain conditions. This is expressed as the following method:

type Interface interface {
    ...
    AutoConnect() bool
}

This feature was designed to let snappy automatically connect plugs in snaps being installed if there is a viable, unique slot on the OS snap that satisfies the interface requirements. If you recall, the OS snap exposes a number of slots for things like network, network-bind and so on. To make the user experience better, when a snap wants to use one of those interfaces the user does not have to connect them explicitly.

Please note that we're going to be conservative in what can be connected automatically. As a rule of thumb auto-connection is allowed if this is a reasonable thing to do and it is not a serious security risk (the interface doesn't hand out too much power).

The complete picture

You can check the complete interface code, with documentation, here. The key thing to take out of this whole article is that interfaces are bits of code that can validate plugs and slots and hand out security snippets.

How this actually gets used and how the snippets should look like, that is for the next post.

Friday, April 22, 2016

snap install --devmode

This is very short post about a particular feature of snappy that many people don't know about.

You can install a snap using "snap install --devmode ...".  The presence of --devmode switches confinement, for that snap only, from enforcing mode where the application is rejected from accessing certain things (or killed, depending on access pattern) to complain mode where the application is allowed to do anything, as the local user could, but appropriate messages are logged to let developers know that something would not normally work.

Development mode is aimed at snap developers to let them understand what their application (or application toolkit) is doing under the hood. We are working on a separate tool that works along --devmode applications, analysing log files and offering suggestions as to which interfaces to use.

Wednesday, April 20, 2016

Snappy interfaces, plugs, slots and connections

This is the second post in a series about snappy interfaces.

In the previous instalment we looked at the basics of interfaces. We used a snap containing the "links" text mode web browser and the "network" plug to get access to the network. We took advantage of the auto-connection feature of the "network" interface to simply install the snap and have it do the right thing.

Today we will focus on connections. A connection is simply an ordered tuple (plug, slot) where plug refers to a particular plug in a particular snap and slot refers to a particular slot in a particular (typically different) snap. Before we really talk about connections I need to explore plugs and slots with a little more depth.

Plugs and slots

As the most essential fact, both plugs and slots have a name and an interface name. In the examples so far this was somewhat shrouded because the plug name and plug interface name had the same value. Let's take all the simplifications away and look at the raw facts.

Recall the snapcraft.yaml file we looked at the last time:

name: links
version: 2.12-1
summary: Web browser running in text mode
description: |
    Links is a text mode WWW browser, similar to Lynx. It displays tables,
    frames, downloads on background, uses HTTP/1.1 keepalive connections.
apps:
    links:
        command: links
        plugs: [network, network-bind]
parts:
    links:
        plugin: nil
        stage-packages:
            - links

Let's focus on just the plug syntax:

apps:
    links:
        command: links
        plugs: [network, network-bind]

What you see above is an abbreviated form. Snappy has many abbreviated forms that make it look nice and are better as long as you understand what they mean. The fully expanded form, having the exact same semantics, looks like this:

apps:
    links:
        command: links
        plugs: [network, network-bind]
plugs:
    network:
        interface: network
    network-bind:
        interface: network-bind

Note that plug name is independent from plug interface name. As a convenience, when the plug name is the same as the interface name the mapping of plug name to interface name can be left out. As another convenience if the plug doesn't require any additional attributes (more on that later) the whole top-level declaration is optional. This is why we could get away with just plugs: [network, network-bind] defined in the app level.

The same is true of slots. Virtually every time we talk about plugs the exact same thing is true for slots. I will not mention that again unless the rule is broken.

Let's recap some of the key facts:
  1. Plugs and slots are objects that have several attributes
  2. Name uniquely identifies a plug or a slot within a particular snap.
  3. Interface name describes the "kind" of plug/slot.
Note that while a plug and a slot can have semi-arbitrary name defined by whoever is making the snap, all interface names are defined within snappy itself. You cannot create a new interface just by inventing a new name. I will describe how to create new interfaces in one of the future instalments.

Connections

So, returning to connections. A connection can be made between a plug and a slot of the same interface type. For example snappy would not allow a connection between the unity7 plug and the network slot. Snappy doesn't allow this. On IOT-like devices you can expect slots using names very different from the interface type they represent. Some made-up examples are "user-button", "led5" or "gpio-12". At the same time you can expect more manual, explicit connections to be established on the command line or through other mechanisms as there will be many possible candidates for a particular slot to form a connection.

Snappy maintains a persistent internal data structure called the state. This is used to record connections as they are established. When you reboot your snappy device the state is used to re-load all connections. Right now there is no difference but once we add support for hooks things will get more interesting. On each reboot the details of particular plugs or slots can be different. For example a hypothetical cheese snap can be connected to the same physical USB camera but due to way USB enumeration works some attributes of the slot that represents that camera might differ (e.g. the particular device that cheese can access can have a different path on the filesystem).

When connections are established the various systems that maintain snappy's security model must be informed. Each security system uses information about plugs, slots, apps, and connections to come up with a set of rules or profiles that encode what each application is or is not allowed to do. I will explore profiles in depth later, for now just remember that profiles are specific to an application. A given snap can have different security profiles for each of its applications based on how plugs and slots are bound to those applications.

Binding plugs and slots to app

So what does a binding look like? Let's say that links grew a bookmark manager app that can be started separately from links itself. Let's also assume that, for tighter security, we will not allow that application to talk to the network. This is how this could be encoded in snapcraft.yaml:

...
apps:
    links:
        command: links
        plugs: [network, network-bind]
    bookmarks:
        command: links-bookmark-editor (fake)
...

Note that only the links app refers to plugs, the bookmarks app does not. If a plug or slot is declared in a snap, but not associated with a specific application they will be implicitly bound to all apps. When a plug or slot is specified by one or more apps, as in the above example, it will be bound only to those applications. Compare that to the following code:

...
apps:
    links:
        command: links
    bookmarks:
        command: links-bookmark-editor (fake)
plugs:
    network:
        interface: network
    network-bind:
        interface: network-bind
...


Here both network and network-bind plugs are not referred from any application explicitly and thus they implicitly bind to all apps. Keep this in mind, the difference is subtle but the effective security is not.

Working with connections

Snappy allows users to change connections using two command line commands: snap connect and snap disconnect. Both of those respond to --help so make sure to read that if you are exploring.
Caveat: as of snapd 2.0.2 connect/disconnect only accept the fully-spelled-out form "snap connect snap:plug snap:slot". Other variants don't work yet, please avoid them for now.
For example, if you installed links as a snap earlier, you could now disconnect it from the network and network-bind slots and see what happens. Let's do so now:

Remember: the order for all of those operations is "snap:plug snap:slot"

$ sudo snap disconnect links:network ubuntu-core:network
$ sudo snap disconnect links:network-bind ubuntu-core:network-bind

After running the two commands links won't be able to use the system calls required for accessing the network and will effectively be offline.

At time of writing, links would not even start correctly, as it would be killed in the attempt to use particular network-related system calls, but I suspect that over time we will tweak the interfaces system to just put apps into an offline sandbox as if you had yanked the network cables and put enough tinfoil around your device to shield it from all signals. I would certainly love to be able to start an arbitrary app without letting it talk to the network.


If you inspect the output of "snap interfaces" you will see that it has changed slightly, the relevant parts now look like this:

Slot                 Plug
:network             -
:network-bind        -
-                    links:network
-                    links:network-bind

This output tells us that both plugs and slots are disconnected. To restore links to an usable state you can re-connect it with the following two commands:

$ sudo snap connect links:network ubuntu-core:network
$ sudo snap connect links:network-bind ubuntu-core:network-bind

That's it for today. Next time we will take a closer look at security systems and how they interact with interfaces internally. This will allow us to explore creating new interfaces for things that don't have one yet. 

As before please feel free to post your comment below. I will gladly answer all questions related to the interface system.

Special thanks to Jonathan Cave for proofreading and improvements!

Snappy, snapcraft and interfaces

This is the first of a series of articles about snappy, specifically focusing on snappy interfaces.

Interfaces are an essential part of snappy. They are the mechanism which allows snaps to reach beyond the default space they are given, to interact with other parts of the system, with other snaps, with the network, with the desktop and more.

Before we begin we should define some terminology. I will do my best to use the right terms each time. During the development of snappy 2.0 series we tried various different terms for what is now known as snappy interfaces. If you've been following the development release for the past few months you may still remember some of the older terms and that can add to the confusion as to what interfaces are. Let's set this straight now:

Snap is the new package format. Snaps hold one or more applications.
App is a runnable executable exposed by a snap. There are command line apps, desktop apps and background apps (aka daemon/service).
Interface is the mechanism with which two snaps can interact with each other. The type of interaction is defined by the interface name. Interfaces can allow snaps to communicate, share particular resources or access particular hardware.
Plug is the consumer part of an interface. Plugs can be connected to slots. Most snaps will define one or more plugs.
Slot is the producer or provider part of an interface. Slots are what plugs connect to. Many slots come directly from the OS snap. Most snaps will not define any slots.

Don't worry if the interface/plug/slot part is still confusing. Let's explore a single example today and see how plugs and slots play a part of the experience. For this purpose I've created a snap with the links text mode web browser.

The entire snapcraft.yaml file for this snap is reproduced below:

name: links
version: 2.12-1
summary: Web browser running in text mode
description: |
    Links is a text mode WWW browser, similar to Lynx. It displays tables,
    frames, downloads on background, uses HTTP/1.1 keepalive connections.
apps:
    links:
        command: links
        plugs: [network, network-bind]
parts:
    links:
        plugin: nil
        stage-packages:
            - links

If you are not familiar with snapcraft I would recommend taking a quick look at the introduction to snapcraft. Even if you haven't used snapcraft yet the content is somewhat self-explanatory. The snap "links" contains one app, "links", which runs the command "links". The snap is comprised of just one part, "links", which is using the "nil" (do nothing extra) plugin to put the "links" Debian package into the stage directory from which our snap is created.

All that this does it puts pre-packaged links executable into a snap. No compiling required!

In Ubuntu, Debian and many other distributions, applications started by a particular user run with the privileges of that user. In other words *every* application that you install from outside of the official repository has complete and unrestricted access to all of your personal files. To your photos. To your browser history. To your saved passwords. To your ssh and gpg keys. Each application you ever installed from a PPA also has complete access to your kernel and to all the other applications. Wow, that's a lot of responsibility. This is why in Ubuntu the trust is put on the Debian / Ubuntu repository. Trustworthy people review the software that is placed there to ensure it doesn't have any nasty code inside.

So why did we have use the two plugs in the snapcraft.yaml above? Because snappy is different. Similarly to how your phone may work, snaps don't get unrestricted access to all of your hardware and personal data.

Recall that snappy runs all apps in a confined sandbox. This is the same technology we use on the Ubuntu phone and tablet. I won't go into details as to how this snadbox works but suffice it to say that apps don't have access to the network or to your personal files default. To give links access to the internet we have to use snappy interfaces.

So what are those interfaces? The network interface allows applications to access the network as a client. This is clearly what links wants to do, it wants to have access to the internet. The network-bind interface allows applications to access the network as a server. Why does links need this? Because apparently it has an option to bind to particular local IP address. It's somewhat unfortunate but we cannot avoid it without patching this feature out of links. Let's not do that today. This is exactly what the statement "plugs: [network, network-bind]" above does.

Please recall that interfaces have two sides, the plug side and the slot side. The plugs are meant for consumer. The slots are meant for producers (or providers). So who's providing network and network-bind on our system? The OS does.

The OS snap, ubuntu-core, has a slot of type network (many plugs will be named after the interface name but those names are independent). When the snap links was installed snappy automatically connected the plug network from the snap links to the slot network from the snap ubuntu-core. The same thing happened for network-bind. Both the network and the network-bind interfaces are designed to automatically-connect to the slots on the OS snap.

You can see that this is true by running the command "snap interfaces" which shows all plugs, slots and their connections.

zyga@zyga-thinkpad-w510:~$ snap interfaces
Slot                 Plug
:firewall-control    -
:home                -
:locale-control      -
:log-observe         -
:mount-observe       -
:network             links
:network-bind        links
:network-control     -
:network-observe     -
:opengl              -
:snapd-control       -
:system-observe      -
:timeserver-control  -
:timezone-control    -
:unity7              -
:x11                 -

Let's have a look at the output. There are two columns, one for slots and one for plugs. In general each plug and slot is displayed as snap:plug or snap:slot but as you can see above there are a number of abbreviations that make the output less repetitive. The Slot column doesn't display the name of the OS snap (ubuntu-core). The Plug column doesn't display the plug name if it is the same as interface name (here it just displays the snap name twice).

Had both of those abbreviations been turned off the relevant portion of the output would look something like this:

zyga@zyga-thinkpad-w510:~$ snap interfaces
Slot                            Plug
ubuntu-core:network             links:network
ubuntu-core:network-bind        links:network-bind

You can install this example snap on your system (make sure to run up-to-date Ubuntu 16.04 on an amd64 architecture) by running "sudo snap install links". After that it should just work, you should be able to run links and browse the web in all of its plain-text glory.

In the next article I will explore connections. Show you how you can disconnect, connect and what happens when you do that.

If you have any questions please leave them as comments below.