SnappyConfinement

Differences between revisions 14 and 15
Revision 14 as of 2014-12-10 18:53:41
Size: 15092
Editor: jdstrand
Comment:
Revision 15 as of 2014-12-10 19:37:37
Size: 15186
Editor: jdstrand
Comment:
Deletions are marked like this. Additions are marked like this.
Line 45: Line 45:
Then the APP_ID for the 'bar' service is `foo_bar_0.1`. Then the APP_ID for the 'bar' service is `foo_bar_0.1`. Notice, this is '<name>/<basename of binary/service name>')
Line 71: Line 71:
 * If the binary is a service, the `snappy-systemd` makes sure `/etc/systemd/system/<service>.service` has `AppArmorProfile=<profilename>` (in the above example, `AppArmorProfile=foo_bar_0.1`) such that the service starts under confinement.  * If the binary is a service, `snappy-systemd` makes sure `/etc/systemd/system/<service>.service` launches the service using `ubuntu-snapp-launch` (snappy-systemd temporarily sets `AppArmorProfile=<profilename>` instead of using the launcher)
Line 94: Line 94:
 * If the binary is a service, the `snappy-systemd` makes sure `/etc/systemd/system/<service>.service` has `AppArmorProfile=<profilename>` (in the above example, `AppArmorProfile=foo_bar_0.1`) such that the service starts under confinement.  * If the binary is a service, `snappy-systemd` makes sure `/etc/systemd/system/<service>.service` launches the service using `ubuntu-snapp-launch` (snappy-systemd temporarily sets `AppArmorProfile=<profilename>` instead of using the launcher)
Line 147: Line 147:
Key ideas for snappy frameworks: Key points regarding snappy frameworks:
Line 149: Line 149:
 * Frameworks provide the mediatio
Line 153: Line 154:
 * The number of snappy frameworks is expected to be relatively low

See the SnappyFrameworkConfinement for more details.

See the [[https://wiki.ubuntu.com/SecurityTeam/Specifications/SnappyConfinement|SnappyFrameworkConfinement specification]] for more details.
Line 158: Line 158:
If the yaml specifies 'binaries' then `bin-path` will expose these binaries to the shell and other apps by launching apps via a specialized launcher that does the following:
 * chdir()s to the app install directory
 * sets miscellaneous environment variables
 * sets up and mount namespace and in that mount namespace, setup a root overlay and /tmp
 * sets up cgroup net_cls for tagging all packets for this app
 * sets up a cgroup for net_cls named foo_bar_0
.1 for tagging all packets for this app
 * sets up iptables chain in 'security' table for the app (uses 'snapps' chain, then app-specific subchains) and add rules to allow from specified apps, and disallow from untagged)
 * apply seccomp rules to only allow binding to specified ports
 * launches the app under confinement

It does this by adding a symlink to a directory in your PATH which points to the launcher. The launcher then calculates the APP_ID to launch the app under. This launcher will setup the environment, adjust the working directory and launch the binary under its !AppArmor profile. For example, consider the following snappy packaging yaml:{{{
If the yaml specifies 'binaries' then `bin-path` will expose these binaries to the shell and other apps by launching apps via a specialized launcher. It does this by adding a symlink to a directory in your PATH which points to the launcher. The launcher then calculates the APP_ID to launch the app under. This launcher will setup the environment, adjust the working directory and launch the binary under its !AppArmor profile. For example, consider the following snappy packaging yaml:{{{
Line 176: Line 166:
`bin-path` will create the `foo/bar` symlink in a directory in your PATH (creating the 'foo' directory as needed. Also, notice this is <name>/<basename of binary name>). When executing that symlink, the launcher will: `bin-path` will create the `foo/bar` symlink in a directory in your PATH (creating the 'foo' directory as needed). This makes it so that the user can run the application using 'bin/bar' and have it launch under the !AppArmor profile `foo_bar_0.1`.

=== ubuntu-snapp-launch ===
Services and binaries are launched under confinement via `ubuntu-snapp-launch`. Consider the following yaml:{{{
name: foo
version: 0.1
...
services:
  - name: bar
    start: bin/bar
    ports:
        required: 80/tcp
    allowed-apps: foo.baz_norf, foo.qux_quux
}}}

when executing this application, the launcher will:
Line 179: Line 184:
 * set up and mount namespace and in that mount namespace, setup a root overlay and /tmp
 * set up a cgroup for net_cls named foo_bar_0.1 for tagging
 * set up iptables chain in 'security' table for the app and add rules to allow from specified apps, and disallow from untagged
 * set up and mount namespace and in that mount namespace
  * sets up a /
overlay (to facilitate developers creating snapps on top of debs from the archive)
  * sets up a
/tmp overlay (so apps all get their own /tmp)
 * set up a cgroup for net_cls named foo_bar for tagging
 * set up iptables chain in 'security' table for the app and add rules to allow from specified apps (in this case, foo.baz_norf and foo.qux_quux), and disallow from untagged
Line 183: Line 190:
 * run the app under the `foo_bar_0.1` profile

Note, as of 2014-12-09, a shell script is temporarily being used instead of a symlink.
 * run the app under the `foo_bar_0.1` !AppArmor profile

Note, as of 2014-12-09, a shell script that only uses `aa-exec` is temporarily being used instead of ubuntu-snapp-launch.
  • Created: 2014-12-05

  • Created by: Jamie Strandboge

  • Contributors: Jamie Strandboge, Marc Deslauriers

  • Packages affected: apparmor, apparmor-easyprof-ubuntu-snappy, click-apparmor, snappy-systemd, click-bin-path, ubuntu-snapp-launch

  • Status: Alpha

Introduction

Snappy confinement is an evolution of the security model for Ubuntu Touch. The basic concepts for confined applications and the AppStore model pertain to snappy applications as well. In short, applications are confined using AppArmor by default and this is achieved through a simple template-based system where policy is extended through the use of policy groups.

It will be most helpful if you are familiar with:

Implementation summary

Snappy uses a simple packaging format that is an evolution of click packaging. Snappy packages use a declarative yaml syntax and defaults to using default AppArmor template and the "networking" policy group. Apps may customize the default behavior in a number of ways via the yaml syntax.

Under the hood, the security manifest is used to describe the confinement of the app. Most apps do not need to specify anything for confinement and snappy will create a security manifest like the following:

{
  "policy_vendor": "ubuntu-snappy",
  "policy_version": 1.3,
  "template": "default",
  "policy_groups": [
    "networking"
  ]
}

The defaults provided may change as the confinement needs evolve. The template policy is found in /usr/share/apparmor/easyprof/ubuntu-snappy/templates/1.3/default and the networking policy is found in /usr/share/apparmor/easyprof/ubuntu-snappy/policy-groups/1.3/networking.

"apparmor"

Security policy in snappy is done through apparmor integration. Internally apparmor integration generates an AppArmor profile based on what is in the snappy packaging yaml. Importantly, the AppArmor profile name is unique to the application binary/service and is achieved by using the concept of an ApplicationId. The APP_ID is the composition of the package name, the service/binary name and package version. The APP_ID takes the form of <pkgname>_<appname>_<version>. For example, if this is in packaging yaml:

name: foo
version: 0.1
...
services:
  - name: bar
    start: bin/bar
    ports:
        required: 80/tcp

Then the APP_ID for the 'bar' service is foo_bar_0.1. Notice, this is '<name>/<basename of binary/service name>')

Applications are launched under confinement through the following:

  • If unspecified in the packaging yaml, snappy will choose the default profile and networking policy group (this may change as snappy involves)

  • Apps may choose to specify an alternate confinement binaries and services by specifying security-template and/or security-policy-groups in the yaml. Eg:

    ...
    services:
      - name: bar
        start: bin/bar
        ports:
            required: 80/tcp
        security-template: nondefault
        security-policy-groups: networking, something, etc
  • For special circumstances, the packaging yaml may also use apparmor in its 'integration' section:

    ...
    services:
      - name: bar
        start: bin/bar
        ports:
            required: 80/tcp
    integration:
      bar:
        apparmor: meta/bar.apparmor
  • aa-clickhook is run on package install and places the resulting profile in /var/lib/apparmor/profiles.

  • If the binary is a service, snappy-systemd makes sure /etc/systemd/system/<service>.service launches the service using ubuntu-snapp-launch (snappy-systemd temporarily sets AppArmorProfile=<profilename> instead of using the launcher)

  • If the binary is a cli binary, it can be launched under confinement via the bin-path mechanism (see below)

  • apparmor policy templates and policy groups are usually shipped via the apparmor-easyprof-ubuntu-snappy package

The default template allows ELF executables, python, perl and shell (with selected corresponding utilities from /bin and /usr/bin), disallows capabilities(7) and enforces application isolation as per the snappy FHS.

"apparmor-profile"

In addition to the above, specialized, hand-crafted confinement may be done via the apparmor-profile integration section. Specifying apparmor-profile will trigger a manual review in the store, and is not needed for normal Snappy apps, but instead is provided for snappy framework and trusted snappy applications.

The aa-profile-hook works similarly to aa-clickhook:

  • The snappy packaging specifies to use 'apparmor-profile' in its 'integration' section, which specifies the profile. Eg:

    ...
    services:
      - name: bar
        start: bin/bar
        ports:
            required: 80/tcp
    integration:
      bar:
        apparmor-profile: meta/bar.profile
  • the profile should reference the AppArmor variables for CLICK_DIR, APP_PKGNAME, APP_APPNAME, APP_VERSION and for profile attachment, just like the apparmor template does with click-apparmor

  • aa-profile-hook is run on install and places the profile in /var/lib/apparmor/profiles (prefixed with 'profile_')

  • If the binary is a service, snappy-systemd makes sure /etc/systemd/system/<service>.service launches the service using ubuntu-snapp-launch (snappy-systemd temporarily sets AppArmorProfile=<profilename> instead of using the launcher)

  • aa-profile-hook is shipped in click-apparmor

  • use of apparmor-profile requires manual review via the store.

To improve maintenace, the profile author can use AppArmor variables to avoid worrying about updating the profile name, the app name, where the app is installed or knowing the package version. Example profile:

#include <tunables/global>

# Specified profile variables
###VAR###

###PROFILEATTACH### (attach_disconnected) {
  #include <abstractions/base>
  #include <abstractions/nameservice>

  # Read-only for the install directory
  @{CLICK_DIR}/@{APP_PKGNAME}/                   r,
  @{CLICK_DIR}/@{APP_PKGNAME}/@{APP_VERSION}/    r,
  @{CLICK_DIR}/@{APP_PKGNAME}/@{APP_VERSION}/**  mrklix,

  # Writable home area
  owner @{HOMEDIRS}/apps/@{APP_PKGNAME}/   rw,
  owner @{HOMEDIRS}/apps/@{APP_PKGNAME}/** mrwklix,

  # Read-only system area for other versions
  /var/lib/apps/@{APP_PKGNAME}/   r,
  /var/lib/apps/@{APP_PKGNAME}/** mrkix,

  # Writable system area only for this version.
  /var/lib/apps/@{APP_PKGNAME}/@{APP_VERSION}/   w,
  /var/lib/apps/@{APP_PKGNAME}/@{APP_VERSION}/** wl,

  ... specialized confinement ...

Example yaml to use the profile:

name: foo
version: 0.1
...
services:
  - name: bar
    start: bin/bar
    ports:
        required: 80/tcp
integration:
  bar:
    apparmor-profile: meta/bar.profile

You may not specify the apparmor and apparmor-profile for the same <appname> (eg, specifying "apparmor-profile": "meta/appname.profile" and "apparmor": "meta/appname.apparmor" is an error).

"apparmor-policy"

apparmor-policy is provided for frameworks to extend security policy (specifically, to add templates and/or policy groups) so that apps that specify the framework may then reference these template/policy groups to access any services/binaries that the framework exposes.

Key points regarding snappy frameworks:

  • Frameworks extend Ubuntu Core in useful ways for apps to use
  • Frameworks provide the mediatio
  • Frameworks are delivered via snaps
  • Because frameworks require additional privilege and are considered trusted (how much depends on what the framework offers), framework authors must partner with Canonical on framework design, security policy verification, acceptance in the store, etc
  • Frameworks are expected to provide significant functionality that is useful to a wide range of apps
  • Multiple snappy frameworks may be installed on the same system

See the SnappyFrameworkConfinement specification for more details.

"bin-path"

If the yaml specifies 'binaries' then bin-path will expose these binaries to the shell and other apps by launching apps via a specialized launcher. It does this by adding a symlink to a directory in your PATH which points to the launcher. The launcher then calculates the APP_ID to launch the app under. This launcher will setup the environment, adjust the working directory and launch the binary under its AppArmor profile. For example, consider the following snappy packaging yaml:

name: foo
version: 0.1
...
binaries:
  - name: bin/bar

bin-path will create the foo/bar symlink in a directory in your PATH (creating the 'foo' directory as needed). This makes it so that the user can run the application using 'bin/bar' and have it launch under the AppArmor profile foo_bar_0.1.

ubuntu-snapp-launch

Services and binaries are launched under confinement via ubuntu-snapp-launch. Consider the following yaml:

name: foo
version: 0.1
...
services:
  - name: bar
    start: bin/bar
    ports:
        required: 80/tcp
    allowed-apps: foo.baz_norf, foo.qux_quux

when executing this application, the launcher will:

  • chdir() to /apps/foo/current
  • set miscellaneous environment variables
  • set up and mount namespace and in that mount namespace
    • sets up a / overlay (to facilitate developers creating snapps on top of debs from the archive)
    • sets up a /tmp overlay (so apps all get their own /tmp)
  • set up a cgroup for net_cls named foo_bar for tagging
  • set up iptables chain in 'security' table for the app and add rules to allow from specified apps (in this case, foo.baz_norf and foo.qux_quux), and disallow from untagged
  • apply seccomp rules to only allow binding to specified ports
  • run the app under the foo_bar_0.1 AppArmor profile

Note, as of 2014-12-09, a shell script that only uses aa-exec is temporarily being used instead of ubuntu-snapp-launch.

Normal usage

As stated, the snappy packaging yaml does not have to do anything to specify the default confinement. Eg, the following yaml:

name: foo
version: 0.1
...
services:
  - name: bar
    start: bin/bar
  - name: baz
    start: bin/baz
  - name: norf
    start: bin/norf

will create the following AppArmor profiles with default confinement:

  • foo_bar_0.1
  • foo_baz_0.1
  • foo_norf_0.1

Advanced usage

While the snappy packaging yaml is intentionally simple and straightforward for app developers, it can also quite flexible for those who need it. For example, consider the following yaml:

name: foo
version: 0.1
...
services:
  - name: normal-service
    start: bin/normal-service
  - name: extra-policy-group-service
    start: bin/extra-policy-group-service
    security-policy-groups: networking, extra
  - name: non-default-template-service
    start: bin/non-default-template-service
    security-template: non-default
binaries:
  - name: bin/normal-binary
  - name: extra-policy-group-binary
    security-policy-groups: networking, extra

snappy will generate the following security manifests:

  • normal-service:

    {
      "policy_vendor": "ubuntu-snappy",
      "policy_version": 1.3,
      "template": "default",
      "policy_groups": [
        "networking"
      ]
    }
  • extra-policy-group-service:

    {
      "policy_vendor": "ubuntu-snappy",
      "policy_version": 1.3,
      "template": "default",
      "policy_groups": [
        "networking",
        "extra"
      ]
    }
  • non-default-template-service:

    {
      "policy_vendor": "ubuntu-snappy",
      "policy_version": 1.3,
      "template": "non-default",
      "policy_groups": [
        "networking"
      ]
    }
  • normal-binary:

    {
      "policy_vendor": "ubuntu-snappy",
      "policy_version": 1.3,
      "template": "default",
      "policy_groups": [
        "networking"
      ]
    }
  • extra-policy-group-binary:

    {
      "policy_vendor": "ubuntu-snappy",
      "policy_version": 1.3,
      "template": "default",
      "policy_groups": [
        "networking",
        "extra"
      ]
    }

Which in turn creates the following AppArmor profiles:

  • foo_normal-service_0.1
  • foo_extra-policy-group-service_0.1
  • foo_non-default-template-service_0.1
  • foo_normal-binary_0.1
  • foo_extra-policy-group-binary_0.1

Debugging

To check to see if you have any denials:

$ sudo journalctl --no-pager -k | grep DEN

A denial will look something like:

apparmor="DENIED" operation="mkdir" profile="foo_bar_0.1" name="/var/lib/foo" pid=637 comm="bar" requested_mask="c" denied_mask="c" fsuid=0 ouid=0

If there are no denials, AppArmor shouldn't be blocking the app.

If there are denials, you can unblock yourself by:

  • modifying the profile in /var/lib/apparmor/profiles that corresponds to your app
  • reload the profile with:

    $ sudo apparmor_parser -r /var/lib/apparmor/profiles/<profile>

Do note that the local modification will not be preserved on package update. If you believe you have found a bug, please file a bug against: https://bugs.launchpad.net/ubuntu/+source/apparmor/+filebug

Helpful degugging commands

  • sudo aa-clickhook -f will regenerate all the apparmor profiles in /var/lib/apparmor/profiles

  • sudo aa-profile-hook -f will regenerate all the apparmor-profile profiles in /var/lib/apparmor/profiles

  • sudo aa-status will show you the profiles loaded in the kernel and what processes are running under them

  • ps Z, ps Z <pid> and ps auxwwZ will show you normal ps output, but with the apparmor label the profile is running under

  • sudo systemctl stop <app>.service and sudo systemctl start <app>.service to stop and start services from /etc/systemd/system (ie, where snappy-systemd puts the service files)

  • This is often helpful when developing your app or policy for it:
    • In one terminal launch (tail kernel log for AppArmor denials):

      $ sudo journalctl -f -k | grep DEN
    • In another (service name is the filename in /etc/systemd/system):

      $ sudo journalctl -f -u <service name>
    • Then launch the app with systemctl like above, or launch manually under confinement with:

      $ aa-exec -p <profile name> -- /apps/<pkgname>/<version>/...

Future work

  • Confining snappy apps (particularly processes requiring root) may use AppArmor-wrapped user namespaces.


CategorySpec

SecurityTeam/Specifications/SnappyConfinement (last edited 2016-11-17 16:29:21 by jdstrand)