SnappyConfinement
⇤ ← Revision 1 as of 2014-12-10 05:09:46
13935
Comment:
|
13957
|
Deletions are marked like this. | Additions are marked like this. |
Line 308: | Line 308: |
---- CategorySpec |
Created: 2014-12-05
Created by: Jamie Strandboge
Contributors: Jamie Strandboge, Marc Deslauriers
Packages affected: apparmor, apparmor-easyprof-ubuntu-snappy, click-apparmor, snappy-systemd
Status: Beta
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 are extended through the use of policy groups.
Please familiarize yourself with the following before proceeding:
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" 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
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:
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 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)
- 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.
SecurityTeam/Specifications/SnappyConfinement (last edited 2016-11-17 16:29:21 by jdstrand)