SnappyConfinement

Differences between revisions 1 and 53 (spanning 52 versions)
Revision 1 as of 2014-12-10 05:09:46
Size: 13935
Editor: jdstrand
Comment:
Revision 53 as of 2016-06-12 16:19:08
Size: 4563
Editor: jdstrand
Comment:
Deletions are marked like this. Additions are marked like this.
Line 5: Line 5:
 * '''Contributors''': [[LaunchpadHome:jdstrand|Jamie Strandboge]], [[LaunchpadHome:mdeslaur|Marc Deslauriers]]
 * '''Packages affected''': apparmor, apparmor-easyprof-ubuntu-snappy, click-apparmor, snappy-systemd
 * '''Status''': Beta
 * '''Contributors''': [[LaunchpadHome:jdstrand|Jamie Strandboge]], [[LaunchpadHome:tyhicks|Tyler Hicks]], [[LaunchpadHome:mdeslaur|Marc Deslauriers]]
 * '''Packages affected''': apparmor, libseccomp, ubuntu-core-security, ubuntu-snappy, ubuntu-core-launcher (historically, click-apparmor)
 * '''Status''': Production
Line 10: Line 10:
Snappy confinement is an evolution of the [[https://wiki.ubuntu.com/SecurityTeam/Specifications/ApplicationConfinement|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 are extended through the use of policy groups. Snappy confinement is an evolution of the [[https://wiki.ubuntu.com/SecurityTeam/Specifications/ApplicationConfinement|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 by default through the use of various technologies and this is achieved through a simple template-based system where policy is extended through the use of interfaces.
Line 12: Line 12:
Please familiarize yourself with the following before proceeding:
 * http://www.ubuntu.com/snappy#core-tour
 * http://developer.ubuntu.com/snappy/filesystem-layout/
 * http://developer.ubuntu.com/snappy/packaging-format-for-apps/
Ubuntu Core 15.04 spec can be viewed here: https://wiki.ubuntu.com/SecurityTeam/Specifications/SnappyConfinement15.04
Line 17: Line 14:
== Implementation summary ==
Snappy uses a simple packaging format that is an evolution of [[https://wiki.ubuntu.com/SecurityTeam/Specifications/ApplicationConfinement/Manifest|click packaging]]. Snappy packages use a declarative [[http://developer.ubuntu.com/snappy/packaging-format-for-apps/|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.
Please see the security whitepaper for the most up to date information on Ubuntu Core series 16: https://developer.ubuntu.com/en/snappy/guides/security-whitepaper/
Line 20: Line 16:
Under the hood, the [[https://wiki.ubuntu.com/SecurityTeam/Specifications/ApplicationConfinement/Manifest|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"
  ]
}
== Debugging ==
When debugging policy issues, the `snappy-debug.security` tool can help. Use `sudo snap install snappy-debug` and then simply launch it to have it follow the logs and provide suggestions:{{{
$ sudo snap install snappy-debug
$ sudo snap connect snappy-debug:log-observe ubuntu-core:log-observe
$ sudo /snap/bin/snappy-debug.security scanlog
...
Line 31: Line 24:
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`. `snappy-debug.security scanlog` will report both !AppArmor and seccomp denials.
Line 33: Line 26:
=== "apparmor" hook ===
The `apparmor` hook is used to generate an !AppArmor profile based on what is in the snappy packaging yaml. Importantly, the profile name is unique to the application binary/writer 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
Alternatively you can use the lowlevel tools to check to see if you have any denials:{{{
$ sudo journalctl --no-pager -k | grep audit
Line 43: Line 30:
Then the APP_ID for the 'bar' service is `foo_bar_0.1`.

Applications are launched under confinement by:
 * If unspecified, `snappy` will choose the `default` profile and `networking` policy group
 * 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
    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
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, the snappy-systemd hook 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 cli binary, it will be launched under confinement via the '`bin-path` hook (see below)
 * apparmor policy templates and policy groups are usually shipped via the apparmor-easyprof-ubuntu-snappy package

The default template allows ELF binaries, 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" hook ===
In addition to the above, a new `apparmor-profile` hook is provided to facilitate specialized, hand-crafted confinement. This hook is not allowed to !AppStore apps, but 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
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, the snappy-systemd hook 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.
 * `aa-profile-hook` is shipped in click-apparmor
 * use of `apparmor-profile` requires manual review via the store.

To improve maintenace, the profile author doesn't have to worry about updating the profile name, the app name, where it is installed or package version and may instead use AppArmor variables in the policy. 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
integration:
  bar:
    apparmor-profile: meta/bar.profile
}}}

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

=== "apparmor-policy" hook ===
The `apparmor-policy` hook 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 ideas for snappy frameworks:
 * Frameworks extend Ubuntu Core in useful ways for apps to use
 * 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
 * The number of snappy frameworks is expected to be relatively low

See the SnappyFrameworkConfinement for more details.

=== "bin-path" hook ===
If the yaml specifies 'binaries' then the `snappy-bin-path` hook 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 up any environment
 * launches the app under confinement

It does this by placing a symlink in `~/snappy-bin` pointing to the launcher. The launcher then queries the system to obtain the pkgname, version and profile name to launch the app under. The launcher will also `~/snappy-bin` is included as part of the user's PATH. The `snappy-bin-path` hook will create a very small shell script in `~/snappy-bin` to 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
}}}

The `snappy-bin-path` hook will create the ~/snappy-bin/foo-bar symlink (notice this is <name>-<basename of binary name>. When executing that symlink, the launcher will:
 * chdir() to /apps/foo/current
 * set TMPDIR="/var/lib/apps/foo/0.1", etc
 * 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.
}}}


== 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/owncloud
  - name: baz
    start: bin/owncloud
  - name: norf
    start: bin/owncloud
}}}

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 ('''TBD'''):{{{
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:{{{
An !AppArmor denial will look something like:{{{
Line 279: Line 34:
If there are no denials, !AppArmor shouldn't be blocking the app. If there are no !AppArmor denials, !AppArmor shouldn't be blocking the app.
Line 281: Line 36:
If there are denials, you can unblock yourself by:
 * modifying the profile in /var/lib/apparmor/profiles that corresponds to your app
If there are !AppArmor denials, you can unblock yourself by:
 * modifying the profile in /var/lib/snapd/apparmor/profiles that corresponds to your app
Line 284: Line 39:
$ sudo apparmor_parser -r /var/lib/apparmor/profiles/<profile> $ sudo apparmor_parser -r /var/lib/snapd/apparmor/profiles/<profile>
Line 286: Line 41:

A seccomp denial will look something like:{{{
audit: type=1326 audit(1430766107.122:16): auid=1000 uid=1000 gid=1000 ses=15 pid=1491 comm="env" exe="/bin/bash" sig=31 arch=40000028 syscall=983045 compat=0 ip=0xb6fb0bd6 code=0x0
}}}

The `syscall=983045` can be resolved with the `scmp_sys_resolver` command (you may also use the `sc-logresolve` command). Eg:{{{
$ scmp_sys_resolver 983045
set_tls
}}}

In general, if there are no seccomp denials, it shouldn't be blocking the app, however do keep in mind that if the app is somehow trying to elevate its privileges (eg, via a setuid executable) the app may receive a `Permission denied` error with no denial (see `PR_SET_NO_NEW_PRIVS` discussion above). If there are seccomp denials, you can unblock yourself by modifying the seccomp file in /var/lib/snappy/seccomp/profiles, then launch your app like normal (the launcher will pick up the change on app invocation).
Line 289: Line 55:
=== Helpful 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 the snappy-systemd hook puts the service files)
=== Helpful degugging commands ===
 * `sudo sysctl -w kernel.printk_ratelimit=0` will disable kernel rate limitingof denials
 * `snappy-debug.security scanlog`: follow /var/log/syslog` and show !AppArmor and seccompg denial
 * `snappy-debug.security disable-rate-limiting`: disable kernel rate limiting
 * `snappy-debug.security reload [<name>.<origin>]`: reloads apparmor security policy into the kernel
 * `snappy-debug.security regenerate [<name>.<origin>]`: regenerate apparmor security policy from snappy packaging
Line 296: Line 62:
  * In one terminal launch (tail kernel log for !AppArmor denials):{{{
$ sudo journalctl -f -k | grep DEN
  * In one terminal launch `snappy-debug.security scanlog`:{{{
$ sudo snappy-debug.security scanlog
Line 299: Line 65:
  * In another (service name is the filename in /etc/systemd/system):{{{
$ sudo journalctl -f -u <service name>
  * In another (if daemon):{{{
$ sudo journalctl -k -u <service name>| grep audit
Line 302: Line 68:
  * Then launch the app with systemctl like above, or launch manually under confinement with:{{{   * Then launch the app normally or launch manually under confinement with:{{{
Line 306: Line 72:
= Future work =
 * Confining snappy apps (particularly processes requiring root) may use !AppArmor-wrapped user namespaces.
----
CategorySpec

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 by default through the use of various technologies and this is achieved through a simple template-based system where policy is extended through the use of interfaces.

Ubuntu Core 15.04 spec can be viewed here: https://wiki.ubuntu.com/SecurityTeam/Specifications/SnappyConfinement15.04

Please see the security whitepaper for the most up to date information on Ubuntu Core series 16: https://developer.ubuntu.com/en/snappy/guides/security-whitepaper/

Debugging

When debugging policy issues, the snappy-debug.security tool can help. Use sudo snap install snappy-debug and then simply launch it to have it follow the logs and provide suggestions:

$ sudo snap install snappy-debug
$ sudo snap connect snappy-debug:log-observe ubuntu-core:log-observe
$ sudo /snap/bin/snappy-debug.security scanlog
...

snappy-debug.security scanlog will report both AppArmor and seccomp denials.

Alternatively you can use the lowlevel tools to check to see if you have any denials:

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

An AppArmor 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 AppArmor denials, AppArmor shouldn't be blocking the app.

If there are AppArmor denials, you can unblock yourself by:

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

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

A seccomp denial will look something like:

audit: type=1326 audit(1430766107.122:16): auid=1000 uid=1000 gid=1000 ses=15 pid=1491 comm="env" exe="/bin/bash" sig=31 arch=40000028 syscall=983045 compat=0 ip=0xb6fb0bd6 code=0x0

The syscall=983045 can be resolved with the scmp_sys_resolver command (you may also use the sc-logresolve command). Eg:

$ scmp_sys_resolver 983045
set_tls

In general, if there are no seccomp denials, it shouldn't be blocking the app, however do keep in mind that if the app is somehow trying to elevate its privileges (eg, via a setuid executable) the app may receive a Permission denied error with no denial (see PR_SET_NO_NEW_PRIVS discussion above). If there are seccomp denials, you can unblock yourself by modifying the seccomp file in /var/lib/snappy/seccomp/profiles, then launch your app like normal (the launcher will pick up the change on app invocation).

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 sysctl -w kernel.printk_ratelimit=0 will disable kernel rate limitingof denials

  • snappy-debug.security scanlog: follow /var/log/syslog` and show AppArmor and seccompg denial

  • snappy-debug.security disable-rate-limiting: disable kernel rate limiting

  • snappy-debug.security reload [<name>.<origin>]: reloads apparmor security policy into the kernel

  • snappy-debug.security regenerate [<name>.<origin>]: regenerate apparmor security policy from snappy packaging

  • This is often helpful when developing your app or policy for it:
    • In one terminal launch snappy-debug.security scanlog:

      $ sudo snappy-debug.security scanlog
    • In another (if daemon):

      $ sudo journalctl -k -u <service name>| grep audit
    • Then launch the app normally or launch manually under confinement with:

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


CategorySpec

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